Implement interpolated strings via String.Concat#19971
Open
charlesroddie wants to merge 2 commits into
Open
Conversation
A string-typed interpolated string is lowered to System.String.Concat of its parts rather than the reflection-based printf engine: a string-typed hole is passed through directly, any other plain hole is converted with `string x`, an aligned/formatted hole with `String.Format(InvariantCulture, ...)`, and a printf-specifier hole with `sprintf`. This removes the reflection dependency on the common path, so these interpolations become trim- and NativeAOT-compatible. This generalizes and replaces the language-version-gated String.Concat optimization (dotnet#16556), which only handled all-string holes: the lowering now applies to every string-typed interpolation, ungated. The reflection path is used only for PrintfFormat/FormattableString-typed interpolation. The syntax tree now carries each hole's formatting explicitly, so a printf specifier no longer leaks into an adjacent literal and alignment is no longer a fake tuple: type SynInterpolatedStringPart = | String of value: string * range: range | FillExpr of fillExpr: SynExpr * formatting: SynInterpolationFormatting type SynInterpolationFormatting = | DotNet of alignment: SynExpr option * format: Ident option | Printf of specifier: string * range: range Behavioural change: plain `{x}` holes now render with invariant culture (the F# `string` operator) rather than the current thread culture, matching `string`. Adds a NativeAOT regression test under tests/AheadOfTime/NativeAOT. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
✅ No release notes required |
Contributor
|
🔍 Tooling Safety Check — Affects-Build-Infra, Affects-Bootstrap, Affects-Compiler-Output, Affects-Test-Tooling
|
e2debcd to
f571df9
Compare
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR implements comment, so that interpolated strings, where possible, are implemented via
String.Concat. The benefits are:There are some problems with the current implementation, and this PR takes a moderate approach, resolving some of them but retaining others to keep a broadly backwards-compatible implementation.
stringorPrintfFormat<...>. The latter is an unfortunate addition designed to allow expressions likeprintf $"...", working around the lack ofprintfunctions (fsharp/fslang-suggestions#1092). This should be marked obsolete whenprintfunctions are added.a) The parser handled these only partially: it left the specifier (e.g.
%f) inside the preceding string component, leaving the compiler step to locate and process it. In this PR the specifier is split off and attached to its hole in a parser helper instead. So it's improved but still messy.b) Any hole with these expressions goes through the previous reflection-based route.
stringare culture-independent. This is adjusted to match existing behaviour, using thestringfunction. This is in keeping with similar changes that have moved towardsstringbehaviour. (See the related discussion ofstringvsToStringbehaviour in fsharp/fslang-suggestions#919.)A NativeAOT test
A test under
tests/AheadOfTime/NativeAOT(wired into the Windows trimming CI job) AOT-publishes a program that uses interpolation. Plain and .NET-format holes ({x},{x:F2},{x,6}) are AOT compatible. Printf-format holes (%d{x}) still route throughsprintf, so they remain reflection-based and fail AOT with IL2026/IL2070/IL3050 — see the commented-out examples in the test.This would be a good place to add other currently AOT-incompatible expressions for similar future fixes.
Notes on the LowerInterpolatedStringToConcat feature flag
This work extends and supersedes the work under the LowerInterpolatedStringToConcat flag, an "optimization that lowers string interpolation into a call to concat iff there are at most 4 string parts and all fill expressions are strings".
The reason why this feature was gated is unclear since it's just an optimization, but it's unclear what to do with this gate here. Options:
stringin culture-independence) is a fix to existing behaviour.