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-Gro T-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 |}

Copilot and 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-actions Bot 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 path Release notes path Description
src/Compiler docs/release-notes/.FSharp.Compiler.Service/11.0.100.md

Copilot and 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-actions github-actions Bot 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 abonie June 12, 2026 10:08
@github-project-automation github-project-automation Bot moved this from New to In Progress in F# Compiler and Tooling Jun 17, 2026
T-Gro and 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 abonie June 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 main Jun 23, 2026
50 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI-Tooling-Check-Bypassed Tooling 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