Skip to content

Allow interpolated strings in constant expressions - #20753

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

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

Conversation

@xperiandri

@xperiandri xperiandri commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Under --langversion:preview, an interpolated string whose holes are non-null constant strings with no alignment or format specifier is a constant expression, as in C# 10: [<Literal>] values, attribute arguments and type provider static arguments accept it instead of reporting FS0267, e.g. [<Obsolete($"Use {nameof getItemV2} instead")>]. Everywhere else, quotations included, interpolated strings are unchanged.

Details

Copilot AI balanced review requested due to automatic review settings October 10, 2026 07:10
@xperiandri
xperiandri requested a review from a team as a code owner October 10, 2026 07:10
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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Diagnostic behavior diverges from the RFC, and type-provider static-argument support lacks direct coverage.

3 open findings
What changed in this PR

Adds preview support for constant interpolated strings across literals, attributes, and type-provider static arguments.

Changes:

  • Adds constant-context tracking and compile-time interpolation folding.
  • Adds feature gating, FS3925, localization, and release notes.
  • Adds component tests for literals, attributes, signatures, diagnostics, and quotations.
File Description
tests/​FSharp.Compiler.ComponentTests/​Language/​ConstantInterpolatedStringsTests.fs Adds feature tests.
tests/​FSharp.Compiler.ComponentTests/​FSharp.Compiler.ComponentTests.fsproj Registers the tests.
src/​Compiler/​xlf/​FSComp.txt.zh-Hant.xlf Adds localized resources.
src/​Compiler/​xlf/​FSComp.txt.zh-Hans.xlf Adds localized resources.
src/​Compiler/​xlf/​FSComp.txt.tr.xlf Adds localized resources.
src/​Compiler/​xlf/​FSComp.txt.ru.xlf Adds localized resources.
src/​Compiler/​xlf/​FSComp.txt.pt-BR.xlf Adds localized resources.
src/​Compiler/​xlf/​FSComp.txt.pl.xlf Adds localized resources.
src/​Compiler/​xlf/​FSComp.txt.ko.xlf Adds localized resources.
src/​Compiler/​xlf/​FSComp.txt.ja.xlf Adds localized resources.
src/​Compiler/​xlf/​FSComp.txt.it.xlf Adds localized resources.
src/​Compiler/​xlf/​FSComp.txt.fr.xlf Adds localized resources.
src/​Compiler/​xlf/​FSComp.txt.es.xlf Adds localized resources.
src/​Compiler/​xlf/​FSComp.txt.de.xlf Adds localized resources.
src/​Compiler/​xlf/​FSComp.txt.cs.xlf Adds localized resources.
src/​Compiler/​FSComp.txt Defines FS3925 and feature text.
src/​Compiler/​Facilities/​LanguageFeatures.fsi Declares the feature case.
src/​Compiler/​Facilities/​LanguageFeatures.fs Gates the preview feature.
src/​Compiler/​Checking/​Expressions/​CheckExpressions.fs Implements constant-context folding.
src/​Compiler/​Checking/​CheckDeclarations.fs Initializes constant-context state.
src/​Compiler/​Checking/​CheckBasics.fsi Exposes the internal context field.
src/​Compiler/​Checking/​CheckBasics.fs Defines the context field.
docs/​release-notes/​.Language/​preview.md Documents the language feature.
docs/​release-notes/​.FSharp.Compiler.Service/​11.0.200.md Records the FCS change.

🧠 Review effort: Balanced

Comment thread src/Compiler/Checking/Expressions/CheckExpressions.fs
Comment thread src/Compiler/Checking/Expressions/CheckExpressions.fs Outdated
Comment thread src/Compiler/FSComp.txt Outdated
xperiandri added a commit to xperiandri/fsharp that referenced this pull request Oct 11, 2026
…-constants-wider-holes

Bring in the review fixes of dotnet#20753: only the first hole that breaks a
rule is reported, FS3925 says "must be a constant", and a type provider
test covers static arguments.

The wider hole rules stay as they are. The first-hole test uses aligned
holes here, because integer holes are valid on this branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@T-Gro T-Gro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🤖 🕵️ AI review — verify independently.

Comment thread src/Compiler/Checking/Expressions/CheckExpressions.fs Outdated
Comment thread src/Compiler/Checking/Expressions/CheckExpressions.fs Outdated
@T-Gro
T-Gro self-requested a review October 13, 2026 11:18
@T-Gro T-Gro added the AI-reviewed PR reviewed by AI review council label Oct 13, 2026

@T-Gro T-Gro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The diff is still way too big - apply the NoBloat instructions and CodeCompaction skill.

Especially the massive code comments would benefit not just with drastic reduction, but ideally with well named functions/arguments/bindings/types making the intention clear.

If a well named piece of code just tells what the comment says - delete the comment.

  • Delete the implementation and test narration around constant context, hole evaluation, accumulated state, and provider fixture behavior. Use names such as tryConstantHoleText, allPreviousHolesWereConstant, and StaticStringValueProvider.
  • Flatten the nested local helpers and mutable accumulator in TcConstantInterpolatedString.
  • Reuse the existing static-string type-provider fixture and compact the separate diagnostic Facts into named theory rows.
  • Keep one concise language-feature release note with syntax and restrictions. Delete the duplicate FCS prose and unchanged-quotation commentary.

@github-project-automation github-project-automation Bot moved this from New to In Progress in F# Compiler and Tooling Oct 14, 2026
xperiandri and others added 4 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>
@xperiandri
xperiandri force-pushed the interpolated-string-constants branch from cc218d6 to 4e4506c Compare October 15, 2026 01:53
@xperiandri

Copy link
Copy Markdown
Contributor Author

Done in 4e4506c, rebased on main. 388 added lines instead of 458, 130 of them generated xlf. Top-level tryConstantHoleText; no local helpers, mutables or comments in TcConstantInterpolatedString; one language release note, so the bot now flags src/Compiler. The helloWorld provider can't be reused: compiled in process it overflows the test host stack in QuotationTranslator, so the fixture is a 41-line stateless provider. AI-review findings fixed and tested: the constant path runs only when the feature is supported (false && ($"{1}" = "1") stays valid), and all holes are type-checked before the first is evaluated (FS0039 for missing is back). In preview, an unevaluated interpolation in a literal is still checked as constant; say if that must go too.

An interpolated string in a constant context is folded quietly. A hole that
does not fold leaves a string-typed stand-in at the hole, which the constant
evaluator rejects with FS0267 when, and only when, it reaches it. So a literal
such as false && ($"{1}" = "1") or if true then Root else $"{1}" stays valid
in preview as well, as it is before (review by T-Gro). FS3925 is dropped: the
error is the existing FS0267 on the first hole that breaks a rule.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@xperiandri

Copy link
Copy Markdown
Contributor Author

Fixed in 76dec45, as a new commit on top. An interpolated string in a constant context is now folded quietly, and a hole that does not fold leaves a stand-in that the constant evaluator rejects with FS0267 at that hole, only if it reaches it. false && ($"{1}" = "1") and if true then "ok" else $"{1}" compile in preview and in 11.2 (test An interpolated string that is not evaluated is not checked as a constant). FS3925 is gone: the error is the existing FS0267 on the first hole that breaks a rule, which also removes its 13 xlf units. The RFC amendment has the same wording (fsharp/fslang-design#859).

@T-Gro T-Gro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🤖🕵️ AI review — verify independently.

ValueNone

let tryConstantHoleText (g: TcGlobals) (formatting: SynInterpolationFormatting) (hole: Expr) =
match formatting, tryEvalConstant g hole with

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🤖🕵️ FS0193 (Attempted to divide by zero) while checking a non-string hole; Debug FCS aborts (exit 134).

[<Literal>]
let Text = $"{1 / 0}"

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

AI-reviewed PR reviewed by AI review council

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

3 participants