Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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