Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/11.0.100.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
* Debug: rework for expressions stepping ([PR #19894](https://github.com/dotnet/fsharp/pull/19894))
* Debug: rework conditional erasure, fix stepping over literals ([PR #19897](https://github.com/dotnet/fsharp/pull/19897))
* Debug: fix if and match condition sequence points ([PR #19932](https://github.com/dotnet/fsharp/pull/19932))
* Surface the synthesized all-fields constructor of F# record types to F# code under the `RecordConstructorSyntax` preview feature, via a new `MethInfo.RecdCtor` case. ([PR #19974](https://github.com/dotnet/fsharp/pull/19974))

### Changed

Expand Down
1 change: 1 addition & 0 deletions docs/release-notes/.Language/preview.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Warn (FS3884) when a function or delegate value is used as an interpolated string argument, since it will be formatted via `ToString` rather than being applied. ([PR #19289](https://github.com/dotnet/fsharp/pull/19289))
* Added `MethodOverloadsCache` language feature (preview) that caches overload resolution results for repeated method calls, significantly improving compilation performance. ([PR #19072](https://github.com/dotnet/fsharp/pull/19072))
* Added `ErrorOnMissingSignatureAttribute` preview language feature: makes FS3888 (compiler-semantic attribute on the `.fs` but not on the `.fsi`) an error instead of a warning. ([Issue #19560](https://github.com/dotnet/fsharp/issues/19560), [PR #19880](https://github.com/dotnet/fsharp/pull/19880))
* Allow constructing a record via its all-fields constructor, e.g. `MyRecord(a, b)`, with positional or named arguments (`RecordConstructorSyntax` preview feature). Accessibility matches `{ ... }` construction. ([Suggestion #722](https://github.com/fsharp/fslang-suggestions/issues/722), [RFC FS-1073](https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1073-record-constructors.md), [PR #19974](https://github.com/dotnet/fsharp/pull/19974))

### Fixed

Expand Down
7 changes: 7 additions & 0 deletions src/Compiler/Checking/AccessibilityLogic.fs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,13 @@ let rec IsTypeAndMethInfoAccessible amap m accessDomainTy ad = function
| FSMeth (_, _, vref, _) -> IsValAccessible ad vref
| MethInfoWithModifiedReturnType(mi,_) -> IsTypeAndMethInfoAccessible amap m accessDomainTy ad mi
| DefaultStructCtor(g, ty) -> IsTypeAccessible g amap m ad ty
| RecdCtor(g, ty) ->
// The synthesized all-fields constructor must be no more accessible than constructing the record
// with '{ ... }' syntax: require the type, its representation and every field to be accessible.
// This stops F# inheriting the C# behaviour where the IL constructor is public regardless of the
// record's 'private'/'internal' representation.
IsTypeAccessible g amap m ad ty &&
((tcrefOfAppTy g ty).TrueInstanceFieldsAsRefList |> List.forall (IsRecdFieldAccessible amap m ad))
#if !NO_TYPEPROVIDERS
| ProvidedMeth(amap, tpmb, _, m) as etmi ->
let access = tpmb.PUntaint((fun mi -> ComputeILAccess mi.IsPublic mi.IsFamily mi.IsFamilyOrAssembly mi.IsFamilyAndAssembly), m)
Expand Down
3 changes: 3 additions & 0 deletions src/Compiler/Checking/AttributeChecking.fs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ let rec GetAttribInfosOfMethod amap m minfo =
| FSMeth (g, _, vref, _) -> vref.Attribs |> AttribInfosOfFS g
| MethInfoWithModifiedReturnType(mi,_) -> GetAttribInfosOfMethod amap m mi
| DefaultStructCtor _ -> []
| RecdCtor _ -> []
#if !NO_TYPEPROVIDERS
// TODO: provided attributes
| ProvidedMeth (_, _mi, _, _m) ->
Expand Down Expand Up @@ -191,6 +192,7 @@ let rec BindMethInfoAttributes m minfo f1 f2 f3 =
| FSMeth (_, _, vref, _) -> f2 vref.Attribs
| MethInfoWithModifiedReturnType(mi,_) -> BindMethInfoAttributes m mi f1 f2 f3
| DefaultStructCtor _ -> f2 []
| RecdCtor _ -> f2 []
#if !NO_TYPEPROVIDERS
| ProvidedMeth (_, mi, _, _) -> f3 (mi.PApply((fun st -> (st :> IProvidedCustomAttributeProvider)), m))
#endif
Expand Down Expand Up @@ -246,6 +248,7 @@ let rec MethInfoHasWellKnownAttribute g (m: range) (ilFlag: WellKnownILAttribute
| ILMeth(_, ilMethInfo, _) -> ilMethInfo.RawMetadata.HasWellKnownAttribute(g, ilFlag)
| FSMeth(_, _, vref, _) -> ValHasWellKnownAttribute g valFlag vref.Deref
| DefaultStructCtor _ -> false
| RecdCtor _ -> false
| MethInfoWithModifiedReturnType(mi, _) -> MethInfoHasWellKnownAttribute g m ilFlag valFlag attribSpec mi
#if !NO_TYPEPROVIDERS
| ProvidedMeth _ -> MethInfoHasAttribute g m attribSpec minfo
Expand Down
5 changes: 4 additions & 1 deletion src/Compiler/Checking/ConstraintSolver.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2185,9 +2185,12 @@ and MemberConstraintSolutionOfMethInfo css m minfo minst staticTyOpt =

| MethInfoWithModifiedReturnType(mi,_) -> MemberConstraintSolutionOfMethInfo css m mi minst staticTyOpt

| MethInfo.DefaultStructCtor _ ->
| MethInfo.DefaultStructCtor _ ->
error(InternalError("the default struct constructor was the unexpected solution to a trait constraint", m))

| MethInfo.RecdCtor _ ->
error(InternalError("the record all-fields constructor was the unexpected solution to a trait constraint", m))

#if !NO_TYPEPROVIDERS
| ProvidedMeth(amap, mi, _, m) ->
let g = amap.g
Expand Down
29 changes: 21 additions & 8 deletions src/Compiler/Checking/InfoReader.fs
Original file line number Diff line number Diff line change
Expand Up @@ -953,14 +953,22 @@ type InfoReader(g: TcGlobals, amap: ImportMap) as this =
else
match tryTcrefOfAppTy g metadataTy with
| ValueNone -> []
| ValueSome tcref ->
tcref.MembersOfFSharpTyconByName
|> NameMultiMap.find ".ctor"
|> List.choose(fun vref ->
match vref.MemberInfo with
| Some membInfo when (membInfo.MemberFlags.MemberKind = SynMemberKind.Constructor) -> Some vref
| _ -> None)
|> List.map (fun x -> FSMeth(g, origTy, x, None))
| ValueSome tcref ->
let declaredCtors =
tcref.MembersOfFSharpTyconByName
|> NameMultiMap.find ".ctor"
Comment thread
T-Gro marked this conversation as resolved.
|> List.choose(fun vref ->
match vref.MemberInfo with
| Some membInfo when (membInfo.MemberFlags.MemberKind = SynMemberKind.Constructor) -> Some vref
| _ -> None)
|> List.map (fun x -> FSMeth(g, origTy, x, None))
// An F# record exposes no *declared* constructor, but its synthesized all-fields constructor is
// callable from C# as 'new MyRecord(f1, f2, ...)'. Under the RecordConstructorSyntax feature we
// surface that same constructor to F# too (it elaborates to a record allocation - see BuildMethodCall).
if g.langVersion.SupportsFeature LanguageFeature.RecordConstructorSyntax && tcref.IsRecordTycon then
declaredCtors @ [ RecdCtor(g, origTy) ]
else
declaredCtors
)

static member ExcludeHiddenOfMethInfos g amap m minfos =
Expand Down Expand Up @@ -1264,6 +1272,11 @@ let rec GetXmlDocSigOfMethInfo (infoReader: InfoReader) m (minfo: MethInfo) =
| ValueSome tcref ->
Some(None, $"M:{tcref.CompiledRepresentationForNamedType.FullName}.#ctor")
| _ -> None
| RecdCtor(g, ty) ->
match tryTcrefOfAppTy g ty with
| ValueSome tcref ->
Some(None, $"M:{tcref.CompiledRepresentationForNamedType.FullName}.#ctor")
Comment thread
T-Gro marked this conversation as resolved.
| _ -> None

#if !NO_TYPEPROVIDERS
| ProvidedMeth _ -> None
Expand Down
17 changes: 14 additions & 3 deletions src/Compiler/Checking/MethodCalls.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1120,9 +1120,14 @@ let rec MakeMethInfoCall (amap: ImportMap) m (minfo: MethInfo) minst args static

| MethInfoWithModifiedReturnType(mi,_) -> MakeMethInfoCall amap m mi minst args staticTyOpt

| DefaultStructCtor(_, ty) ->
| DefaultStructCtor(_, ty) ->
mkDefault (m, ty)

| RecdCtor(g, ty) ->
let tcref = tcrefOfAppTy g ty
let tinst = argsOfAppTy g ty
mkRecordExpr g (RecdExpr, tcref, tinst, tcref.TrueInstanceFieldsAsRefList, args, m)

#if !NO_TYPEPROVIDERS
| ProvidedMeth(amap, mi, _, m) ->
let isProp = false // not necessarily correct, but this is only used post-creflect where this flag is irrelevant
Expand Down Expand Up @@ -1264,9 +1269,15 @@ let rec BuildMethodCall tcVal g amap isMutable m isProp minfo valUseFlags minst
else
warning(Error(FSComp.SR.tcDefaultStructConstructorCall(), m))
else
if not (TypeHasDefaultValue g m ty) then
if not (TypeHasDefaultValue g m ty) then
errorR(Error(FSComp.SR.tcDefaultStructConstructorCall(), m))
mkDefault (m, ty), ty)
mkDefault (m, ty), ty

// Build a record allocation from a call to the synthesized all-fields constructor of an F# record.
| RecdCtor (g, ty) ->
let tcref = tcrefOfAppTy g ty
let tinst = argsOfAppTy g ty
mkRecordExpr g (RecdExpr, tcref, tinst, tcref.TrueInstanceFieldsAsRefList, allArgs, m), ty)

let ILFieldStaticChecks g amap infoReader ad m (finfo : ILFieldInfo) =
CheckILFieldInfoAccessible g amap m ad finfo
Expand Down
4 changes: 3 additions & 1 deletion src/Compiler/Checking/NameResolution.fs
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,9 @@ let rec TrySelectExtensionMethInfoOfILExtMem m amap apparentTy (actualParent, mi
| ProvidedMeth(amap,providedMeth,_,m) ->
ProvidedMeth(amap, providedMeth, Some pri,m) |> Some
#endif
| DefaultStructCtor _ ->
| DefaultStructCtor _ ->
None
| RecdCtor _ ->
None

/// Select from a list of extension methods
Expand Down
7 changes: 5 additions & 2 deletions src/Compiler/Checking/NicePrint.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1785,10 +1785,13 @@ module InfoMemberPrinting =
let amap = infoReader.amap

match methInfo with
| DefaultStructCtor _ ->
let prettyTyparInst, _ = PrettyTypes.PrettifyInst amap.g typarInst
| DefaultStructCtor _ ->
let prettyTyparInst, _ = PrettyTypes.PrettifyInst amap.g typarInst
let resL = PrintTypes.layoutTyconRef denv methInfo.ApparentEnclosingTyconRef ^^ wordL punctuationUnit
prettyTyparInst, resL
| RecdCtor _ ->
let prettyTyparInst, _ = PrettyTypes.PrettifyInst amap.g typarInst
prettyTyparInst, layoutMethInfoCSharpStyle amap m denv methInfo methInfo.FormalMethodInst
| FSMeth(_, _, vref, _) ->
let prettyTyparInst, resL = PrintTastMemberOrVals.prettyLayoutOfValOrMember { denv with showMemberContainers=true } infoReader typarInst vref
prettyTyparInst, resL
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/Checking/OverloadResolutionCache.fs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ let rec computeMethInfoHash (minfo: MethInfo) : int =
| FSMeth(_, _, vref, _) -> HashingPrimitives.combineHash (hash vref.Stamp) (hash vref.LogicalName)
| ILMeth(_, ilMethInfo, _) -> HashingPrimitives.combineHash (hash ilMethInfo.ILName) (hash ilMethInfo.DeclaringTyconRef.Stamp)
| DefaultStructCtor(_, _) -> hash "DefaultStructCtor"
| RecdCtor(_, _) -> hash "RecdCtor"
| MethInfoWithModifiedReturnType(original, _) -> computeMethInfoHash original
#if !NO_TYPEPROVIDERS
| ProvidedMeth(_, mb, _, _) ->
Expand Down
Loading
Loading