Skip to content

Accept number, char, bool and %s holes in constant interpolated strings - #20754

Draft
xperiandri wants to merge 5 commits into
dotnet:mainfrom
xperiandri:interpolated-string-constants-wider-holes
Draft

xperiandri wants to merge 5 commits into
dotnet:mainfrom
xperiandri:interpolated-string-constants-wider-holes

Conversation

@xperiandri

@xperiandri xperiandri commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #20753

A constant interpolated string also accepts integer, decimal, char and bool holes and a plain %s hole. F# formats these with the invariant culture, so the folded text is the run-time text: $"v{2}-{true}" is "v2-True". Float holes stay rejected, because their text depends on the runtime that runs the compiler (-0.0 is 0 on .NET Framework and -0 on .NET).

Details

xperiandri added a commit to xperiandri/fsharp that referenced this pull request Oct 10, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

❗ Release notes required

You can open this PR in browser to add release notes: open in github.dev

@xperiandri,

Caution

No release notes found for the changed paths (see table below).

Please make sure to add an entry with an informative description of the change as well as link to this pull request, issue and language suggestion if applicable. Release notes for this repository are based on Keep A Changelog format.

The following format is recommended for this repository:

`* . (PR #XXXXX)`

See examples in the files, listed in the table below or in th full documentation at https://fsharp.github.io/fsharp-compiler-docs/release-notes/About.html.

If you believe that release notes are not necessary for this PR, please add NO_RELEASE_NOTES label to the pull request.

Change path Release notes path Description
`src/Compiler` docs/release-notes/.FSharp.Compiler.Service/11.0.200.md No release notes found or release notes format is not correct

✅ Found changes and release notes in following paths:

Change path Release notes path Description
`src/Compiler/Facilities/LanguageFeatures.fsi` docs/release-notes/.Language/preview.md

xperiandri and others added 5 commits October 15, 2026 01:29
An interpolated string whose holes are non-null constant strings without
alignment or format specifiers now folds to its text where F# requires a
constant: [<Literal>] values (also in signatures), attribute arguments and
type provider static arguments. It was rejected with FS0267, so a constant
built from other constants needed '+'.

The checker marks those three contexts in TcEnv and folds the string there.
Everywhere else, quotations included, interpolated strings are still lowered
to String.Concat. A hole that breaks a rule reports FS3925 at the hole; a hole
that is not a constant reports FS0267 at the hole.

Gated on the ConstantInterpolatedStrings preview feature.

Language suggestion dotnet#1347
RFC FS-1352

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RFC FS-1352 reports the rule error on the first hole that breaks a rule.
Later holes are still type-checked, but are no longer evaluated as
constants, so '$"{1}{2}"' gives FS3925 once instead of once per hole.

Reword FS3925 to the RFC's "must be a constant": the message is shown
when a hole keeps the string from being one.

Cover type provider static arguments, after 'const' and through a
literal, with a provider that returns its string static argument.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review follow-up. The hole text comes from a top-level tryConstantHoleText,
every hole is type-checked before the first one is evaluated (so a later
undefined name is still reported), and the constant path runs only when the
ConstantInterpolatedStrings feature is supported: before the preview language
version an interpolated string in a literal is checked as before, so a branch
that is never evaluated, such as false && ($"{1}" = "1"), stays valid.

The type provider test uses a small fixture that checks the argument it
receives: the helloWorld provider overflows the stack of the test host when it
is compiled in process. One language release note remains.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Unlike C#, F# formats integers, decimals, characters and Booleans in
interpolated strings with the invariant culture, so their text is known at
compile time. A constant interpolated string now also accepts such holes, and a
plain '%s' hole on a constant string, as asked in the RFC FS-1352 discussion:
the folded text is the text 'string' gives at run time ("True" for true,
"1.50" for 1.50m).

Float holes stay excluded: their text depends on the runtime the compiler runs
on (-0.0 is "0" on .NET Framework and "-0" on .NET). Enum holes stay excluded
too.

RFC FS-1352 amendment

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@xperiandri
xperiandri force-pushed the interpolated-string-constants-wider-holes branch from 1e9c891 to 280dd47 Compare October 15, 2026 02:21

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

1 participant