Skip to content

Fix FSI pretty printing for anonymous records - #19919

Merged
T-Gro merged 12 commits into
mainfrom
fix/issue-6116
Jun 23, 2026
Merged

Fix FSI pretty printing for anonymous records#19919
T-Gro merged 12 commits into
mainfrom
fix/issue-6116

Conversation

@T-Gro

@T-GroT-Gro commented Jun 9, 2026

Copy link
Copy Markdown
Member

Fixes#6116

FSI printed anonymous records with { ... } (nominal record syntax) instead
of {| ... |}. Now the printer detects anonymous record types and uses the
correct delimiters, plus struct {| ... |} for struct anonymous records.

// before{ Name ="Alice"; Age =30}// after{| Name ="Alice"
Age =30|}

Copilotand others added 5 commits June 9, 2026 08:14
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Anonymous records were rendered with { } braces, identical to nominal records, because FSharpType.IsRecord returns true for both. Detect the anonymous case via the CLR name prefix `<>f__AnonymousType` and thread an isAnonRecord flag through ValueInfo.RecordValue / recordValueL / makeRecordL so they emit the already-defined leftBraceBar / rightBraceBar punctuation literals ({| / |}).
For anonymous records use a semicolon-separated horizontal layout (semiListL) instead of the vertical aboveListL used by nominal records - this matches how anon records are written in source.
Struct anonymous records (compiled to value-type variants of <>f__AnonymousType) are detected at the same place via reprty.IsValueType, carried on ValueInfo.RecordValue as isStruct, and rendered with the existing struct keyword layout inside recordValueL (mirroring how tupleValueL handles TupleType.Value).
The leftBraceBar / rightBraceBar literal definitions were moved out of the #if COMPILER block in TaggedText so they are also available in the FSharp.Core build of sformat.fs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…6116)
Address CODE-QUALITY verifier feedback:
1. Replace `RecordValue of isAnonRecord: bool * isStruct: bool * ...` with a
3-case `RecordKind = Nominal | AnonReference | AnonStruct` so the
impossible (isAnonRecord=false, isStruct=true) state is unrepresentable.
Construction and consumption now use exhaustive matches.
2. Revert `semiListL` back to `aboveListL` for anon records. The
list-combinator change was outside the original bug scope; the layout
engine already chooses single-line rendering when content fits, so the
only meaningful diff for anon records remains the brace tokens.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
After reverting recordValueL to aboveListL, multi-field anon records print across multiple lines. Split single-line substring assertions into opener/closer pairs (mirroring the nominal-record test) so the multi-line layout is accepted.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actionsBot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

❗ Release notes required

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


✅ Found changes and release notes in following paths:

Change pathRelease notes pathDescription
src/Compilerdocs/release-notes/.FSharp.Compiler.Service/11.0.100.md

Copilotand others added 2 commits June 9, 2026 15:19
…ting (#6116)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot added the AI-Tooling-Check-Bypassed Tooling check: non-fork PR, not diff-analyzed label Jun 9, 2026
)
Regenerate FSI printing baselines under tests/fsharp/core/printing/ so they
reflect the new (correct) anon-record value printing introduced by PR #19919
(fix for issue #6116). Five baselines now show `= {| AnonRecordField2 = 11 |}`
instead of `= { AnonRecordField2 = 11 }`; nominal-record value lines are
unchanged.
Also update tests/fsharp/core/anon/{lib.fs,test.fsx} assertions whose runtime
sprintf `%A` expectations encoded the old anon-record rendering, including
the struct anon record case whose ToString() now prefixes `struct {| ... |}`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@T-Gro
T-Gro requested a review from abonieJune 12, 2026 10:08
@github-project-automationgithub-project-automationBot moved this from New to In Progress in F# Compiler and ToolingJun 17, 2026
T-Groand others added 3 commits June 22, 2026 10:31
Resolve the tests/AheadOfTime/Trimming/check.ps1 size-baseline conflict by
re-running the trimming publish and recording the measured sizes.
Also shrink the FSharp.Core codegen added by #6116: make ReflectUtils.RecordKind
a plain enum instead of a union (it is only pattern-matched, never compared),
which removes the generated equality/comparison/Tag/ToString members.
Measured net9.0 trimmed sizes (CI-calibrated against the unchanged
FSharpMetadataResource test = 7609344):
FSharp.Core.dll: 314880 -> 312832 (feature cost +1024 over base)
StaticLinkedFSharpCore .dll: 9172992 -> 9170944
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Local macOS publish over-reports FSharp.Core.dll trimmed size by ~1536 bytes
vs the CI Windows build (the embedded F# signature resource differs by platform),
so the FSharpMetadataResource test is not a valid calibration anchor. Adopt the
exact sizes reported by the failing CI run.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@T-Gro
T-Gro requested a review from abonieJune 23, 2026 13:07
@T-Gro
T-Gro enabled auto-merge (squash) June 23, 2026 13:07
@T-Gro
T-Gro merged commit 378941c into mainJun 23, 2026
50 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI-Tooling-Check-BypassedTooling check: non-fork PR, not diff-analyzed

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

FSI pretty print of values shows the same text for records and anonymous records

3 participants

@T-Gro@abonie