Repository navigation
Allow interpolated strings in constant expressions - #20753
xperiandri wants to merge 5 commits into
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
❗ Release notes requiredYou can open this PR in browser to add release notes: open in github.dev 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)`
If you believe that release notes are not necessary for this PR, please add NO_RELEASE_NOTES label to the pull request.
|
There was a problem hiding this comment.
🟡 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
…-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
left a comment
There was a problem hiding this comment.
🤖 🕵️ AI review — verify independently.
T-Gro
left a comment
There was a problem hiding this comment.
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, andStaticStringValueProvider. - 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.
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>
cc218d6 to
4e4506c
Compare
|
Done in 4e4506c, rebased on main. 388 added lines instead of 458, 130 of them generated xlf. Top-level |
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>
|
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. |
T-Gro
left a comment
There was a problem hiding this comment.
🤖🕵️ AI review — verify independently.
| ValueNone | ||
|
|
||
| let tryConstantHoleText (g: TcGlobals) (formatting: SynInterpolationFormatting) (hole: Expr) = | ||
| match formatting, tryEvalConstant g hole with |
There was a problem hiding this comment.
🤖🕵️ FS0193 (Attempted to divide by zero) while checking a non-string hole; Debug FCS aborts (exit 134).
[<Literal>]
let Text = $"{1 / 0}"

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
false && ($"{1}" = "1")stays valid. Before the preview language version nothing changes.