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

Interpolated string handler: inconsistent use of AppendFormatted when appending constants #76391

Open
mgravell opened this issue Dec 12, 2024 · 0 comments
Labels
Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead

Comments

@mgravell
Copy link
Member

mgravell commented Dec 12, 2024

Context: custom interpolated string handler, with the intent to perform bespoke operations (think "SQL auto parameterization")

If (and only if) all formatter tokens are constant strings, the string is reduced to a constant expression instead of using the expected AppendFormatted calls.

Version Used:

.NET 9.0.100 (current)

Steps to Reproduce:

The first Utils.Write here is the unexpected outcome; contrast this to a: the similar usage including a constant integer, and b: the similar usage using a variable string, or a variable string and variable integer (which all use AppendFormatted)

using System;
using System.Runtime.CompilerServices;

// constants
const int c_id = 42;
const string c_name = "abc";

// variables
int v_id = c_id;
string v_name = c_name;

// constants, string only
Utils.Write($"c_name: {c_name}"); // <== STRING!!
// constants, string and integer
Utils.Write($"c_id: {c_id}, c_name: {c_name}"); // (interpolated)

// variables, string only
Utils.Write($"v_name: {v_name}"); // (interpolated)
// variables, string and integer
Utils.Write($"v_id: {v_id}, v_name: {v_name}"); // (interpolated)

public static class Utils
{
    public static void Write(ref CustomInterpolatedStringHandler value)
        => Console.WriteLine("Interpolated");
    public static void Write(string value)
        => Console.WriteLine("String");
}
[InterpolatedStringHandler]
public ref struct CustomInterpolatedStringHandler(int x, int y)
{
    public void AppendLiteral(string value) { }
    public void AppendFormatted<T>(T value) { }
}

Actual Behavior:

The first call uses Write(string) with the value concatenated at compile, as shown on sharplab.io

Expected Behavior:

Reduction occurs later; there's a question here as to whether it should still fold and prefer string in the case then the handler is a DefaultInterpolatedStringHandler and a similar string API exists.

Impact:

Custom in AppendFormatted scenarios are not reliable if the token turns out to be constant. In particular, it means that it is hard to see whether this resolves to DoThing(string) or DoThing(ref CustomInterpolatedStringHandler), which may have significant impact if the intent of CustomInterpolatedStringHandler is complex

obj.DoThing($"""
    some complex
    code with {Formatted} and
    non formatted {Could.Be.Far.Away.Regions} that
    may be miles away
    """);

(are Formatted and Regions constant expressions? we don't know, and neither does the reader)

Alternative Thought:

Instead of "fixing" this, maybe there should be a compiler warning if this could be a risk; perhaps all of:

  1. an interpolated string is encountered
  2. the interpolated string is being reduced to a constant expression by the compiler
  3. the receiving argument is not string or DefaultInterpolatedStringHandler

However, I guess that "3" depends on overload resolution having already occurred; maybe it should be

  1. there is a competing usable overload that takes an interpolated string handler other than DefaultInterpolatedStringHandler
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

No branches or pull requests

1 participant