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

New Feature: Constant Interpolated Strings (Initial PR) #45396

Conversation

kevinsun-dev
Copy link
Contributor

@kevinsun-dev kevinsun-dev commented Jun 23, 2020

This feature is about enabling string constants to be made out of interpolated strings, where the interpolated string itself is made up of constants.

public class C
{
    const string S1 = $"Hello world";
    const string S2 = $"Hello{" "}World";
    const string S3 = $"{S1} Kevin, welcome to the team!";
}

Added in this PR:

  • Support simple constant interpolated strings
  • Updated tests

@gafter
Copy link
Member

gafter commented Jun 26, 2020

I would think the simplest way of constructing the resulting string, including handling of escaping, would be to use string.Format on the format string and all of the values of the interpolations. I think that could be done after processing everything rather than at the same time, separating the constant folding implementation from the rest of the semantic analysis. #Closed

@kevinsun-dev kevinsun-dev changed the title Working Constant Interpolated Strings ($"simple") New Feature: Constant Interpolated Strings (Initial PR) Jun 29, 2020
@kevinsun-dev kevinsun-dev marked this pull request as ready for review June 29, 2020 15:27
@kevinsun-dev kevinsun-dev requested review from a team as code owners June 29, 2020 15:27
@kevinsun-dev kevinsun-dev self-assigned this Jun 29, 2020
@@ -25,6 +25,10 @@ private BoundExpression BindInterpolatedString(InterpolatedStringExpressionSynta
var stringType = GetSpecialType(SpecialType.System_String, diagnostics, node);
var objectType = GetSpecialType(SpecialType.System_Object, diagnostics, node);
var intType = GetSpecialType(SpecialType.System_Int32, diagnostics, node);

// PROTOTYPE follow up with Neal on the implementation
Copy link
Member

@jaredpar jaredpar Jun 29, 2020

Choose a reason for hiding this comment

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

Suggested change
// PROTOTYPE follow up with Neal on the implementation
// PROTOTYPE(CONSTISTR) follow up with Neal on the implementation
``` #Closed

Copy link
Member

Choose a reason for hiding this comment

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

This has been done, you can remove the comment :)


In reply to: 447263586 [](ancestors = 447263586)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, the comment has been removed.


In reply to: 447335348 [](ancestors = 447335348,447263586)

@@ -54,6 +58,7 @@ private BoundExpression BindInterpolatedString(InterpolatedStringExpressionSynta
{
alignment = GenerateConversionForAssignment(intType, BindValue(interpolation.AlignmentClause.Value, diagnostics, Binder.BindValueKind.RValue), diagnostics);
var alignmentConstant = alignment.ConstantValue;
constantEnabled = false;
if (alignmentConstant != null && !alignmentConstant.IsBad)
Copy link
Member

@jaredpar jaredpar Jun 29, 2020

Choose a reason for hiding this comment

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

One potential benefit of going the string.Format route is that it could allow us to consider supporting alignment as that would come for free. We should keep that in mind when discussing that aspect of the implementation. #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.

Yup, talked it over with Fred. The alignment support for strings that can only be composed of constants may be outweighed by this implementation's default support for ropes.


In reply to: 447264298 [](ancestors = 447264298)

Comment on lines 30 to 31
ConstantValue? resultConstant = null;
bool constantEnabled = true;
Copy link
Member

@jaredpar jaredpar Jun 29, 2020

Choose a reason for hiding this comment

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

Think the code would read better if the names of these paired values matched better. For instance resultConstant and isResultConstant #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.

Thanks for the suggestion, changes made.


In reply to: 447267474 [](ancestors = 447267474)

@@ -92,20 +98,50 @@ private BoundExpression BindInterpolatedString(InterpolatedStringExpressionSynta
}

builder.Add(new BoundStringInsert(interpolation, value, alignment, format, null));
if (value.ConstantValue != null)
{
if (!value.ConstantValue.IsString || value.ConstantValue.IsBad || value.ConstantValue.IsNull)
Copy link
Member

@jaredpar jaredpar Jun 29, 2020

Choose a reason for hiding this comment

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

Right now the logic to determine if this value is constant is spread out to four places: the format check, the alignment check, the constant check and the type of constant. Consider just combining it into a single check here:

if (value.ConstantValue?.IsString == true &&
    !value.ConstantValue.IsBad &&
    !value.ConstantValue.IsNull &&
    interpolation.FormatClaues is null && 
    interpolation.AlignmentClause is null) 
{
   ...
}

#Closed

Copy link
Member

Choose a reason for hiding this comment

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

The first 3 can be collapsed to a single pattern: value.ConstantValue is { IsString: true, IsBad: false, IsNull: false }


In reply to: 447268625 [](ancestors = 447268625)

Copy link
Member

@333fred 333fred Jun 30, 2020

Choose a reason for hiding this comment

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

And the last 2 as well: interpolation is { FormatClauses : null, AlignmentClause: null }


In reply to: 447343672 [](ancestors = 447343672,447268625)

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 also bail out early if !constantEnabled.


In reply to: 447343824 [](ancestors = 447343824,447343672,447268625)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for all the suggestions, I've incorporated them into commit 16.


In reply to: 447347863 [](ancestors = 447347863,447343824,447343672,447268625)

@@ -92,20 +98,50 @@ private BoundExpression BindInterpolatedString(InterpolatedStringExpressionSynta
}

builder.Add(new BoundStringInsert(interpolation, value, alignment, format, null));
if (value.ConstantValue != null)
{
if (!value.ConstantValue.IsString || value.ConstantValue.IsBad || value.ConstantValue.IsNull)
Copy link
Member

@jaredpar jaredpar Jun 29, 2020

Choose a reason for hiding this comment

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

Why is null special cased here as a non-constant? That is a legal string constant value. Please add a PROTOTYPE comment to follow up on that. #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.

If we're talking about value.ConstantValue, that was null checked to prevent a whole host of NullPointerExceptions. If we're taking about the extra case in the if statement beneath it, I'd honestly forgotten if it was merely out of an abundance of caution or not. Removed it in commit 16, lets see if it works.


In reply to: 447269087 [](ancestors = 447269087)

@@ -286,6 +286,14 @@ internal partial class BoundBinaryOperator
}
}

internal partial class BoundInterpolatedString
{
public override ConstantValue? ConstantValue
Copy link
Member

@333fred 333fred Jun 30, 2020

Choose a reason for hiding this comment

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

What actually uses this property? #Closed

Copy link
Member

Choose a reason for hiding this comment

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

Ah, nevermind.


In reply to: 447344161 [](ancestors = 447344161)

@@ -3990,7 +3990,7 @@ static void F()
}

[WorkItem(976, "https://github.com/dotnet/roslyn/issues/976")]
[Fact]
[Fact(Skip = "PROTOTYPE(CONSTISTR)")]
Copy link
Member

@333fred 333fred Jun 30, 2020

Choose a reason for hiding this comment

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

What is failing about this test? Consider recording that in the skip comment. #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.

Recorded the reason in the skip comment, is this the correct format?


In reply to: 447344626 [](ancestors = 447344626)

Copy link
Member

Choose a reason for hiding this comment

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

That's fine, thanks.


In reply to: 447771212 [](ancestors = 447771212,447344626)

@@ -3212,7 +3212,7 @@ void M()
}

[WorkItem(1065661, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1065661")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsIntroduceVariable)]
[Fact(Skip = "PROTOTYPE(CONSTISTR)"), Trait(Traits.Feature, Traits.Features.CodeActionsIntroduceVariable)]
Copy link
Member

@333fred 333fred Jun 30, 2020

Choose a reason for hiding this comment

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

Same here. #Closed

@@ -3462,6 +3462,33 @@ static void Main()
source.Append(source1);
CompileAndVerify(source.ToString(), expectedOutput: "58430604");
}

[Fact]
public void ConstantInterpolatedStringsSimple()
Copy link
Member

@333fred 333fred Jun 30, 2020

Choose a reason for hiding this comment

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

Please add a prototype comment for testing invalid scenarios as well. #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.

Added tests for invalid scenarios.


In reply to: 447344980 [](ancestors = 447344980)

@333fred
Copy link
Member

333fred commented Jun 30, 2020

Done review pass (commit 15) #Closed

@kevinsun-dev kevinsun-dev requested a review from 333fred June 30, 2020 19:57
}

[Fact]
public void ConstantInterpolatedStringsError()
Copy link
Member

Choose a reason for hiding this comment

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

We'll need more tests, but it's a good start. The rest can wait for test plan review.

Copy link
Member

Choose a reason for hiding this comment

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

Think there are some we can add in advance of the test plan review: used in attributes, default parameter values, used across assembly boundaries, used as fields, etc ... Also the always popular: lang ver checks.

Those can be done in a separate PR but should get them done before a test plan review

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.

LGTM (commit 17)

}

[Fact]
public void ConstantInterpolatedStringsError()
Copy link
Member

Choose a reason for hiding this comment

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

Think there are some we can add in advance of the test plan review: used in attributes, default parameter values, used across assembly boundaries, used as fields, etc ... Also the always popular: lang ver checks.

Those can be done in a separate PR but should get them done before a test plan review

@kevinsun-dev kevinsun-dev merged commit d381843 into dotnet:features/interpolated-string-constants Jun 30, 2020
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.

4 participants