Record constructors (FS-1073)#19974
Conversation
Records compile to a class whose all-fields constructor is callable from C#
(new MyRecord(a, b)) but not from F#, which only allows { Field = ... } syntax.
This adds a RecordConstructorSyntax preview feature that surfaces that constructor
to F# too, supporting positional and named arguments.
Implemented via a new MethInfo.RecdAllFieldsCtor case surfaced by InfoReader for
record tycons; it elaborates through the existing mkRecordExpr path, so there is
no codegen or overload-resolution change. Accessibility mirrors { } construction
(the ctor is no more accessible than the record's representation/fields), so the
C# behaviour of a public IL constructor bypassing a private record is not inherited.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
❗ Release notes requiredYou can open this PR in browser to add release notes: open in github.dev
|
Shorter name; a record has exactly one synthesized constructor, so the 'AllFields' qualifier is not needed to disambiguate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The feature surfaces only the all-fields constructor. A struct record's zero-init default and a [<CLIMutable>] record's IL parameterless .ctor must remain non-callable from F#; both 'Point()' and 'R()' are rejected (FS0501). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…terless ctor A struct record's 'Point()' is default (zero) initialization, not a real constructor; only [<CLIMutable>] emits an actual parameterless .ctor. Name the two tests accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
rangeOfMethInfo had no RecdCtor case, so it fell through to ArbitraryValRef (None) and GoToDefinition on a positional record-constructor call navigated nowhere. Add a RecdCtor arm returning the record type's range, mirroring DefaultStructCtor. Adds FSharp.Compiler.Service.Tests smoke tests: go-to-definition lands on the record type, the tooltip mentions the type, and find-all-references links the constructor call to the type declaration. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Regression guard: the positional constructor has no field labels, so a [<RequireQualifiedAccess>] record must construct without a spurious diagnostic. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
🔍 Tooling Safety Check — Affects-Compiler-Output
|
…073)
Library exposes a record and an inline constructor function; the app constructs
records positionally and via the inline function. The new syntax is gated behind
RECORD_CTOR_FEATURE / --langversion:preview for local builds, with a classic { }
fallback so SDK-compiler scenarios still build. Both branches elaborate to the
same record-allocation node, so the pickled representation is unchanged and the
matrix exercises both directions (feature-enabled consumer, older consumer).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The constructor is not a declared member, so it rides on the record's
representation visibility through a signature: available when the .fsi exposes
the representation, rejected (FS1133, like { }) when the .fsi hides it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ents Per review: rename the per-feature RECORD_CTOR_FEATURE define to the reusable USES_PREVIEW_COMPILER, and remove the explanatory comments so the addition is compact. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| <Compile Include="ModuleReaderCancellationTests.fs" /> | ||
| <Compile Include="EditorTests.fs" /> | ||
| <Compile Include="Symbols.fs" /> | ||
| <Compile Include="RecordConstructorTests.fs" /> |
There was a problem hiding this comment.
One more test angle please - can you assess the quoted representation and difference between {} and explicit .ctor call?
Can go on with https://github.com/dotnet/fsharp/blob/ae00c30e997ae54e8a68a095849e95efa0056bef/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/ExpressionQuotations/QuotationRendering/QuotationRenderingTests.fs and regenerate corresponding quoted baseline.
A positional record constructor quotes as NewRecord (R, Value (1), Value (2)),
identical to { A = 1; B = 2 } - both lower to the same node before quotation
translation, so no constructor call appears in the quotation. Uses
Console.WriteLine + Expr.ToString() to avoid sprintf/printf.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tional-ctor # Conflicts: # src/Compiler/Facilities/LanguageFeatures.fs # src/Compiler/Facilities/LanguageFeatures.fsi
main added a CSharpExtensionTypeDisplay parameter to layoutMethInfoCSharpStyle and updated its callers; the RecdCtor arm (new in this branch) was auto-merged with the old call. Pass extTypeDisplay to match the sibling arms. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements FS-1073. F# records compile to a class whose all-fields constructor is callable from C# (
new MyRecord(a, b)) but not from F#, which only permits{ Field = … }. This adds aRecordConstructorSyntaxpreview language feature that surfaces that constructor to F#, with positional and named arguments.Notes:
{ }construction, so aprivate/internalrecord's representation is not bypassed (unlike C#'s public IL constructor).--langversion:preview.