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

[C#] Make interpolated strings including string or char constants as optimized as + operator (for string type) #72308

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

tats-u
Copy link

@tats-u tats-u commented Feb 28, 2024

VB: #72611

This PR targets at only those evaluated as string (not string handler or formatter types)

@dotnet-issue-labeler dotnet-issue-labeler bot added Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead labels Feb 28, 2024
@tats-u tats-u force-pushed the string-interpolation-string-char branch 2 times, most recently from f36dc21 to 6b9d9b7 Compare March 13, 2024 14:50
@tats-u tats-u force-pushed the string-interpolation-string-char branch from 6b9d9b7 to 0b63661 Compare March 14, 2024 10:32
@tats-u tats-u marked this pull request as ready for review March 14, 2024 14:57
@tats-u tats-u requested a review from a team as a code owner March 14, 2024 14:57
@tats-u tats-u marked this pull request as draft March 17, 2024 06:20
@tats-u tats-u force-pushed the string-interpolation-string-char branch from 51a6a8c to 27b6fa5 Compare March 17, 2024 14:48
@tats-u tats-u marked this pull request as ready for review March 17, 2024 21:11
@333fred
Copy link
Member

333fred commented Mar 19, 2024

Also, let's separate out the VB changes. These are both fairly large changes, and we would want to be able to revert them separately if issues are discovered.

@tats-u tats-u force-pushed the string-interpolation-string-char branch from a4a1070 to d7d542f Compare March 20, 2024 07:13
@tats-u tats-u changed the title Make sure to meld string or char constants in expression in interpolated string into adjacent literal & Use String.Concat if possible in interpolated string in VB [C#] Make interpolated strings including string or char constants as optimized as + operator Mar 20, 2024
@tats-u tats-u marked this pull request as draft March 20, 2024 09:44
@tats-u tats-u force-pushed the string-interpolation-string-char branch from d7d542f to 2355e52 Compare March 24, 2024 00:33
@tats-u tats-u marked this pull request as ready for review March 24, 2024 00:33
@tats-u tats-u force-pushed the string-interpolation-string-char branch 2 times, most recently from ff64036 to e98eb2d Compare March 24, 2024 05:26
@tats-u
Copy link
Author

tats-u commented Mar 24, 2024

"Compiler request rejected"

@CyrusNajmabadi
Copy link
Member

"Compiler request rejected"

This often means the change made to the compiler broke it, and prevents it from being able to compile itself.

@tats-u
Copy link
Author

tats-u commented Mar 24, 2024

This often means the change made to the compiler broke it, and prevents it from being able to compile itself.

I am glad to know that It is due to the bootstrap tests.
It looks like we need more test cases to move the bugs that only appear in the bootstrap tests to the unit tests, where bugs are easier to be debugged.

@tats-u
Copy link
Author

tats-u commented Mar 24, 2024

I have no idea about where must be fixed as of now.

@333fred
Copy link
Member

333fred commented Mar 25, 2024

@tats-u there's a large number of errors in the bootstrap build that look like this:

error CS1570: XML comment has badly formed XML -- 'The 'member' start tag on line 1982 position 85 does not match the end tag of '_3080fe4098134113891e0b8f72e3c74a'.' 

There's also an analyzer error that didn't exist before:

error AD0001: Analyzer 'Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer' threw an exception of type 'System.ArgumentOutOfRangeException' with message 'Index was out of range. Must be non-negative and less than the size of the collection.
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: chunkLength
   at System.Text.StringBuilder.ToString()
   at Microsoft.CodeAnalysis.SymbolDisplayExtensions.ToDisplayString(ArrayBuilder`1 parts) in /_/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayExtensions.cs:line 80
   at Microsoft.CodeAnalysis.CSharp.SymbolDisplay.ToDisplayString(ISymbol symbol, SemanticModel semanticModelOpt, Int32 positionOpt, SymbolDisplayFormat format, Boolean minimal) in /_/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplay.cs:line 66
   at Microsoft.CodeAnalysis.CSharp.Symbols.PublicModel.Symbol.Microsoft.CodeAnalysis.ISymbol.ToDisplayString(SymbolDisplayFormat format) in /_/src/Compilers/CSharp/Portable/Symbols/PublicModel/Symbol.cs:line 166
   at Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer.PerCompilationAnalyzer.AnalyzeAttribute(OperationAnalysisContext context)
   at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c.<ExecuteOperationAction>b__53_0(ValueTuple`2 data) in /_/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs:line 682
   at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info, CancellationToken cancellationToken) in /_/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs:line 1185

This is almost certainly a fallout of your change. What I would do is start with investigating those; fixing them and adding tests for the missing scenarios will likely resolve any other errors, or make them clearer.

@jjonescz
Copy link
Member

@tats-u tats-u requested review from dibarbet and a team as code owners April 14, 2024 14:35
@tats-u tats-u requested a review from a team as a code owner April 14, 2024 14:35
@tats-u tats-u force-pushed the string-interpolation-string-char branch from 0391843 to 378b699 Compare April 14, 2024 14:36
@tats-u tats-u marked this pull request as draft April 14, 2024 14:39
@tats-u tats-u force-pushed the string-interpolation-string-char branch 2 times, most recently from 1dfb61f to 113cf59 Compare April 20, 2024 10:16
@tats-u
Copy link
Author

tats-u commented Apr 20, 2024

Sorry for very late reply, but I managed to fix the missing code causing the failure in the bootstrap test without doing local bootstrap test. (I just added some extra mean test cases and some were failed)

See also https://github.com/dotnet/roslyn/blob/main/docs/contributing/Bootstrap%20Builds.md#investigating
Build.cmd -bootstrap -bootstrapConfiguration Debug

Thank you for the information, but it seems that -bootstrapConfiguration option has been removed (unavailable), doesn't it?

I added a new feature: null constant literals in interpolated strings are now removed or just replaced with empty strings.

$"{null}" // ""
$"foo{null}bar{null}" // "foobar"

Anyway, although I have to add some extra test cases, but my code is now ready for reviewed. Thank you in advance.

@tats-u tats-u marked this pull request as ready for review April 20, 2024 12:11
@tats-u tats-u force-pushed the string-interpolation-string-char branch from 9a14c73 to 4005f1e Compare April 22, 2024 11:12
@tats-u
Copy link
Author

tats-u commented Apr 22, 2024

I finished my work. Optimizations for handlers or formattable strings have too many things to be determined. (especially what types or methods should be trusted)

@tats-u
Copy link
Author

tats-u commented Apr 22, 2024

If you want me to squash commits, I will do.

@tats-u tats-u requested a review from 333fred April 22, 2024 14:15
@tats-u tats-u changed the title [C#] Make interpolated strings including string or char constants as optimized as + operator [C#] Make interpolated strings including string or char constants as optimized as + operator (for string type) Apr 24, 2024
@tats-u
Copy link
Author

tats-u commented Apr 24, 2024

I created a draft of a PoC for the further optimization only for the combination of CultureInfo and DefaultStringHandler, but I have no idea on how to use string.Create in unit tests.

https://github.com/tats-u/roslyn/compare/string-interpolation-string-char...tats-u:roslyn:string-interpolation-string-char-handler?expand=1

I think I should create another PR whose commits are built on the top of this one because the content of this PR is stable.

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.

Done review pass. Tests are not looked at, but since they are currently passing, there's clearly a test gap.

Comment on lines 227 to 228
IsTreatedAsLiteralInStringConcatenation(fillin.Value) ||
fillin.Value.Type is { SpecialType: SpecialType.System_String }
Copy link
Member

Choose a reason for hiding this comment

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

The formatting of this makes it pretty hard to read. Consider breaking this out into a separate variable before to make it more understandable.

Copy link
Author

Choose a reason for hiding this comment

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

I would like to postpone the evaluation of this function as late as possible.
I will split the if statement into two.
!(...) will be an independent if statement with the De Morgan's law applied.

Comment on lines 253 to 271
if (canHideOptimization && fillin is { Alignment: null, Format: null, Value.ConstantValueOpt: { } constantValueOpt })
{
switch (constantValueOpt)
{
case { IsChar: true, CharValue: char ch }:
stringBuilder.Append(escapeInterpolatedStringLiteral(ch.ToString()));
continue;

case { IsString: true, StringValue: var str }:
stringBuilder.Append(escapeInterpolatedStringLiteral(str ?? ""));
continue;

case { IsNull: true }:
// null literal for string? type
continue;
}
Copy link
Member

Choose a reason for hiding this comment

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

We can't do this generally. We can do it for interpolated strings that are directly observed as string, but for IFormattable or FormattableString, this will change the observable semantics of the returned formattable string (as you can call GetArguments() and do post-processing on them if you want). If this is what you meant by the canHideOptimizations parameter, then I would suggest calling that parameter something like usingDirectlyAsString instead. Also, please leave a comment with this explanation in the code.

Copy link
Author

Choose a reason for hiding this comment

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

@333fred
Should we consider FormattableString.Invariant?

usingDirectlyAsString

You seem to have thought "using" is better than "used". If so I will follow you because there do not seem to be a better candidate that is not much longer.

Copy link
Author

Choose a reason for hiding this comment

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

observedAsString?

// TODO: Last argument could be true for some cases
// e.g. - ((FormattableString)$"Now: {DateTime.Now}").ToString(CultureInfo.GetCulture("some-lang"))
// - FormattableString.Invariant($"Now: {DateTime.Now}")
MakeInterpolatedStringFormat((BoundInterpolatedString)conversion.Operand, out format, out expressions, false);
Copy link
Member

Choose a reason for hiding this comment

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

Please use the parameter name for false.

Copy link
Author

Choose a reason for hiding this comment

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

It is better to just remove it (because it is an optional parameter)?

Comment on lines 21 to 23
// TODO: Last argument could be true for some cases
// e.g. - ((FormattableString)$"Now: {DateTime.Now}").ToString(CultureInfo.GetCulture("some-lang"))
// - FormattableString.Invariant($"Now: {DateTime.Now}")
Copy link
Member

Choose a reason for hiding this comment

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

Let's just not deal with these.

Copy link
Author

@tats-u tats-u Jul 20, 2024

Choose a reason for hiding this comment

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

The second one is really bad as you pointed out.
Even the first one? I got it.

Copy link
Author

@tats-u tats-u Jul 21, 2024

Choose a reason for hiding this comment

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

@333fred How about these ones?
FormattableString.Invariant will not abuse this optimization and outside code cannot observe the optimization.

const string CompanyNameConst = "Your Company";
FormattableString.Invariant($"Copyright (c) {year} {CompanyNameConst}")
// FormattableString.Invariant(FormattableStringFactory.Create("Copyright (c) {0} {1}", [year, "Your Company"]))
// ↓
// FormattableString.Invariant(FormattableStringFactory.Create("Copyright (c) {0} Your Company", [year]))

Copy link
Member

Choose a reason for hiding this comment

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

Let's just not deal with these.

Copy link
Author

Choose a reason for hiding this comment

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

I've given up trying to optimize that case.

reusable = default;
}

foreach (var part in parts)
Copy link
Member

Choose a reason for hiding this comment

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

I did not get very far into reviewing this foreach. It's extremely complicated, with method-level state implicitly flowing around (I have absolutely no idea what reusable is or what it's tracking). It needs be deeply commented, and not method-level state tracking. I would recommend starting by clarifying what reusable actually means, and making the merge methods be static local functions, to force all state to be explicitly shown.

Copy link
Author

Choose a reason for hiding this comment

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

@333fred

no idea what reusable is

It is used to retain nodes of AppendLiteral method calls and string literals that have not been joined to others yet.
They can be reused (output as is) until merged to others.
Maybe I should not have to define it as a tuple form.

making the merge methods be static local functions

The reason why I have not made it static is just because I have to use _factory. The other part is static.
Is it true that you would like me to convert it to an additional parameter of the local function?
I do not care about whether _factory is a captured field or a function parameter.

Copy link
Author

Choose a reason for hiding this comment

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

Nodes that may be retained by reusable are just previous part of the loop variable part.


foreach (var part in parts)
{
if (part is not BoundCall { Method.Name: not null, } call)
Copy link
Member

Choose a reason for hiding this comment

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

When is this true? What does it mean?

Copy link
Author

@tats-u tats-u Jul 21, 2024

Choose a reason for hiding this comment

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

I assume mainly BoundDynamicCall.
It should be treated as is.
{ ... } may be misplaced kindness because neither of Method or Name is nullable.
I have not known what belongs to BoundDynamicCall instead of BoundCall.

Copy link
Author

@tats-u tats-u Jul 28, 2024

Choose a reason for hiding this comment

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

I mean BoundDynanmicInvocation.

{
case BoundInterpolatedString.AppendFormattedMethod:
{
// never be BoundLiteral (can be BoundLocal), so see ConstantValueOpt instead
Copy link
Member

Choose a reason for hiding this comment

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

What will never be a BoundLiteral? The argument certainly can be, and that's the only thing I can think of that you might be saying? Are you saying it will never be a BoundLiteral of a string?

Copy link
Author

Choose a reason for hiding this comment

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

Are you saying it will never be a BoundLiteral of a string?

I forgot whether or not, but I think I meant so at that time.
I will fix the comment.

int increasedLiteralLength = 0;

BoundExpression createAppendLiteralCallFromBoundLiteral(BoundLiteral literal, BoundExpression receiver) =>
_factory.InstanceCall(receiver, BoundInterpolatedString.AppendLiteralMethod, literal);
Copy link
Author

@tats-u tats-u Jul 21, 2024

Choose a reason for hiding this comment

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

#74274 removed this InstanceCall. What should I write instead of this?
How can I generate a MethodSymbol instance from the method name ("AppendLiteral")?

Copy link
Author

Choose a reason for hiding this comment

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

I will retore this definition:

public BoundExpression InstanceCall(BoundExpression receiver, string name, bool disallowExpandedNonArrayParams, BoundExpression arg0)
    => MakeInvocationExpression(BinderFlags.None, this.Syntax, receiver, name, disallowExpandedNonArrayParams, [arg0], this.Diagnostics);

Copy link
Member

Choose a reason for hiding this comment

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

#74275 - we can't restore this method.

Copy link
Author

@tats-u tats-u Jul 23, 2024

Choose a reason for hiding this comment

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

I'm afraid I have no idea about how I should get a MethodSymbol instance of an AroundLiteral method instead of restoring the InstanceCall method because it does not belong to any interfaces (cannot use WellKnownMember). Could you tell me a hint or how to do it instead?

Copy link
Member

Choose a reason for hiding this comment

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

The concern is that we can't do overload resolution at all here. Since we don't need overload resolution to find the specific AppendLiteral(string) method, we can just get the method symbol and use it directly.

Copy link
Member

Choose a reason for hiding this comment

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

I'd probably condense all of that into a single pattern, but otherwise it looks about right.

Copy link
Author

Choose a reason for hiding this comment

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

Do I have to aggregate some conditional expressions, not only chain them with or and ||?

Copy link
Member

Choose a reason for hiding this comment

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

I don't really know what that question means?

Copy link
Author

@tats-u tats-u Jul 30, 2024

Choose a reason for hiding this comment

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

I mean whether you prefer the following

         if (
             candidate is not MethodSymbol { Parameters: [{ Type.SpecialType: SpecialType.System_String }], DeclaredAccessibility: Accessibility.Public} methodCandidate
                 || methodCandidate.IsStatic
          )
          {
              continue;
          }
         return methodCandidate;

to the following:

         if (
             candidate is not MethodSymbol methodCandidate
                 || methodCandidate.IsStatic
                 || methodCandidate is not { Parameters: [{ Type.SpecialType: SpecialType.System_String }] }
                 || methodCandidate.DeclaredAccessibility != Accessibility.Public
          )
          {
              continue;
          }
         return methodCandidate;

NOTE: we can't write the following due to dotnet/csharplang#8323:

         if (
             candidate is not MethodSymbol { Parameters: [{ Type.SpecialType: SpecialType.System_String }], DeclaredAccessibility: Accessibility.Public} methodCandidate
                 or { IsStatic: false }
          )
          {
              continue;
          }
         return methodCandidate;

The following is OK. Do you prefer it to the above 2?

         if (
             candidate is not (
                     MethodSymbol { Parameters: [{ Type.SpecialType: SpecialType.System_String }], DeclaredAccessibility: Accessibility.Public} methodCandidate
                         and { IsStatic: false }
                 )
          )
          {
              continue;
          }
         return methodCandidate;

Copy link
Member

@333fred 333fred Jul 31, 2024

Choose a reason for hiding this comment

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

None of the above. I mean

if (candidate is MethodSymbol { IsStatic: false, Parameters: [{ Type.SpecialType: SpecialType.System_String }], DeclaredAssessibility: Assessibility.Public } methodCandidate)
{
    return methodCandidate;
}

@tats-u tats-u force-pushed the string-interpolation-string-char branch 3 times, most recently from ea4e845 to 1f3482b Compare July 28, 2024 13:26
@tats-u tats-u force-pushed the string-interpolation-string-char branch from 1f3482b to 4c3fac4 Compare July 31, 2024 14:46
@333fred
Copy link
Member

333fred commented Jul 31, 2024

@tats-u please do not force-push branches under code review. It prevents us from doing a diff review, and means we have to start from fresh again. I hope to have some time later this week to take another look at the changes, but the force push is going to mean it takes significantly more time and will delay my review.

@tats-u
Copy link
Author

tats-u commented Aug 1, 2024

@333fred Oh, sorry. That's why I can find many roughly-written commit messages in the commit history of this repo. Now I don't have to rebase main until a new conflict occurs. I'll try to minimize amending commits and to avoid mix amending commits and rebasing to main in one force-push.
However, I'd like you to know I had to erase the following unexpected and ugly change from the history. That couldn't be avoided.

$ diff -up <(git diff 2bfbc1e1b1194dbfeae2751821f70d3082669e9e~2 2bfbc1e1b1194dbfeae2751821f70d3082669e9e) <(git diff ea4e845~2 ea4e845)
--- /dev/fd/63  2024-08-02 00:09:58.000000000 +0900
+++ /dev/fd/62  2024-08-02 00:09:58.000000000 +0900
@@ -46,15 +46,9 @@ index 46d37fdd2d5..87a3d3f6c64 100644

              if (node.OperatorKind is BinaryOperatorKind.Utf8Addition)
 diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs
-index ce475336445..8f2297e231b 100644
+index ce475336445..6f2840ea6a2 100644
 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs
 +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs
-@@ -1,4 +1,4 @@
--// Licensed to the .NET Foundation under one or more agreements.
-+// Licensed to the .NET Foundation under one or more agreements.=> (MethodSymbol)factory.WellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_DefaultInterpolatedStringHandler__AppendLiteral));
- // The .NET Foundation licenses this file to you under the MIT license.
- // See the LICENSE file in the project root for more information.
-
 @@ -18,6 +18,9 @@ private BoundExpression RewriteInterpolatedStringConversion(BoundConversion conv
              Debug.Assert(conversion.ConversionKind == ConversionKind.InterpolatedString);
              BoundExpression format;

If I had to apply the change in the latest main next time, should I merge main instead of rebase main?


P.S. May these help you:

From the last commit after the triple force-push:

$ diff -up <(git diff  ea4e845~2 ea4e845) <(git diff HEAD~2 HEAD)
--- /dev/fd/63  2024-08-01 23:50:43.000000000 +0900
+++ /dev/fd/62  2024-08-01 23:50:43.000000000 +0900
@@ -31,22 +31,21 @@ index 0a15fecb093..ebdb9101b19 100644
                              }
                          case SyntaxKind.InterpolatedStringText:
 diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs
-index 46d37fdd2d5..87a3d3f6c64 100644
+index 46d37fdd2d5..84047bd73f6 100644
 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs
 +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs
-@@ -116,7 +116,9 @@ public BoundExpression VisitBinaryOperator(BoundBinaryOperator node, BoundUnaryO
+@@ -116,7 +116,8 @@ public BoundExpression VisitBinaryOperator(BoundBinaryOperator node, BoundUnaryO
              {
                  Debug.Assert(node.Type.SpecialType == SpecialType.System_String, "Non-string binary addition should have been handled by VisitConversion or VisitArguments");
                  ImmutableArray<BoundExpression> parts = CollectBinaryOperatorInterpolatedStringParts(node);
 -                return LowerPartsToString(data, parts, node.Syntax, node.Type);
 +                // We do not have to consider custom handlers or formatters here thanks to the above Debug.Assert, so now we have only to take expression trees into account.
-+                var canHideOptimization = !_inExpressionLambda;
-+                return LowerPartsToString(data, parts, node.Syntax, node.Type, canHideOptimization);
++                return LowerPartsToString(data, parts, node.Syntax, node.Type, usingDirectlyAsString: !_inExpressionLambda);
              }

              if (node.OperatorKind is BinaryOperatorKind.Utf8Addition)
 diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs
-index ce475336445..6f2840ea6a2 100644
+index ce475336445..1c660ae0fee 100644
 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs
 +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs
 @@ -18,6 +18,9 @@ private BoundExpression RewriteInterpolatedStringConversion(BoundConversion conv
@@ -385,7 +384,7 @@ index ce475336445..6f2840ea6a2 100644
 +
 +            foreach (BoundExpression part in parts)
 +            {
-+                // BoundDynamicInvoke etc.
++                // BoundDynamicInvocation etc.
 +                if (part is not BoundCall call)
 +                {
 +                    // cannot be merged

From the latest force-push:

$ diff -up <(git diff 1f3482b~2  1f3482b) <(git diff ea4e845~2 ea4e845)
--- /dev/fd/63  2024-08-02 00:23:03.000000000 +0900
+++ /dev/fd/62  2024-08-02 00:23:03.000000000 +0900
@@ -46,7 +46,7 @@ index 46d37fdd2d5..87a3d3f6c64 100644

              if (node.OperatorKind is BinaryOperatorKind.Utf8Addition)
 diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs
-index ce475336445..1c660ae0fee 100644
+index ce475336445..6f2840ea6a2 100644
 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs
 +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs
 @@ -18,6 +18,9 @@ private BoundExpression RewriteInterpolatedStringConversion(BoundConversion conv
@@ -385,7 +385,7 @@ index ce475336445..1c660ae0fee 100644
 +
 +            foreach (BoundExpression part in parts)
 +            {
-+                // BoundDynamicInvocation etc.
++                // BoundDynamicInvoke etc.
 +                if (part is not BoundCall call)
 +                {
 +                    // cannot be merged

The current first commit is the change as of the previous review (4005f1e).
You have only to check the change by the current second commit.

$ diff -up <(git diff  4005f1ed9b7f1cbdbac0c4ef27e4c482750f7779~22 4005f1ed9b7f1cbdbac0c4ef27e4c482750f7779) <(git diff HEAD~2 HEAD~)
--- /dev/fd/63  2024-08-03 13:57:17.000000000 +0900
+++ /dev/fd/62  2024-08-03 13:57:17.000000000 +0900
@@ -31,10 +31,10 @@ index 0a15fecb093..ebdb9101b19 100644
                              }
                          case SyntaxKind.InterpolatedStringText:
 diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs
-index 8a57e0daae1..072c8a6782d 100644
+index 46d37fdd2d5..87a3d3f6c64 100644
 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs
 +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs
-@@ -115,7 +115,9 @@ public BoundExpression VisitBinaryOperator(BoundBinaryOperator node, BoundUnaryO
+@@ -116,7 +116,9 @@ public BoundExpression VisitBinaryOperator(BoundBinaryOperator node, BoundUnaryO
              {
                  Debug.Assert(node.Type.SpecialType == SpecialType.System_String, "Non-string binary addition should have been handled by VisitConversion or VisitArguments");
                  ImmutableArray<BoundExpression> parts = CollectBinaryOperatorInterpolatedStringParts(node);
@@ -46,7 +46,7 @@ index 8a57e0daae1..072c8a6782d 100644

              if (node.OperatorKind is BinaryOperatorKind.Utf8Addition)
 diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs
-index a148a9ee88d..3bc9369f7d7 100644
+index ce475336445..af3b69b2b6e 100644
 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs
 +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs
 @@ -18,7 +18,10 @@ private BoundExpression RewriteInterpolatedStringConversion(BoundConversion conv
@@ -749,7 +749,7 @@ index 338ad31210c..93b7e280620 100644
      }
  }
 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs
-index 2d3ef41415b..369eecc85af 100644
+index c05cf0acb7a..0b72676b4f0 100644
 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs
 +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs
 @@ -4,6 +4,7 @@
@@ -804,7 +804,7 @@ index 2d3ef41415b..369eecc85af 100644
  }
  ");
          }
-@@ -13543,6 +13536,81 @@ .locals init (System.Linq.Expressions.ParameterExpression V_0)
+@@ -13541,6 +13534,81 @@ .locals init (System.Linq.Expressions.ParameterExpression V_0)
  ");
          }

@@ -886,7 +886,7 @@ index 2d3ef41415b..369eecc85af 100644
          [Theory]
          [CombinatorialData]
          public void CustomHandlerUsedAsArgumentToCustomHandler(bool useBoolReturns, bool validityParameter, [CombinatorialValues(@"$""""", @"$"""" + $""""")] string expression)
-@@ -16087,11 +16155,9 @@ public void InterpolatedStringsAddedUnderObjectAddition2(string expression)
+@@ -16050,11 +16118,9 @@ public void InterpolatedStringsAddedUnderObjectAddition2(string expression)
              CompileAndVerify(comp, expectedOutput: @"
  (
  value:1
@@ -900,9 +900,9 @@ index 2d3ef41415b..369eecc85af 100644
  value:3
  }
  ");
-@@ -18569,5 +18635,261 @@ public struct A
-                 //     public A(int x, A a = M($"{1}")) { }
-                 Diagnostic(ErrorCode.ERR_BadArgRef, @"$""{1}""").WithArguments("1", "ref").WithLocation(8, 29));
+@@ -18983,5 +19049,261 @@ public override string ToString()
+                 Diagnostic(ErrorCode.ERR_ConversionWithInterface, "C1").WithArguments("C1.implicit operator C1(System.IFormattable)").WithLocation(18, 37)
+             );
          }
 +
 +        [Theory]
@@ -1163,7 +1163,7 @@ index 2d3ef41415b..369eecc85af 100644
      }
  }
 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs
-index 08ff7b26dc4..5786584bd89 100644
+index 4f5656dafe8..894b30bf51b 100644
 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs
 +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs
 @@ -2057,13 +2057,11 @@ public void TargetTypedInterpolationHoles(string expression)
@@ -1210,7 +1210,7 @@ index 08ff7b26dc4..5786584bd89 100644
  }
  ");
      }
-@@ -11636,11 +11628,9 @@ public void InterpolatedStringsAddedUnderObjectAddition2(string expression)
+@@ -11635,11 +11627,9 @@ public void InterpolatedStringsAddedUnderObjectAddition2(string expression)
          CompileAndVerify(comp, expectedOutput: @"
  (
  value:1

}
else
{
BoundLiteral? literal = null;
Copy link
Author

@tats-u tats-u Aug 3, 2024

Choose a reason for hiding this comment

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

I wonder if we should temporarily retain not only BoundLiteral but also other BoundExpression that has string ConstantValueOpt.
Those other than BoundLiteral are not ideal but fine as arguments of AppendLiteral.
If we retain such values, extra operations on PooledStringBuilder will be reduced.

@jjonescz
Copy link
Member

jjonescz commented Aug 5, 2024

That's why I can find many roughly-written commit messages in the commit history of this repo.

I'll try to minimize amending commits

We squash PRs in the compiler layer of this repo, so no intermediate commits of compiler PRs are visible in the commit history. It's best to avoid any amending and force pushes.

If I had to apply the change in the latest main next time, should I merge main instead of rebase main?

Yes, please do merge main instead of rebase main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers 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.

5 participants