diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md index cdb8195a13e..f1e760930c0 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -164,13 +164,14 @@ * Support for the `` XML documentation tag: at compile time, documentation is copied from an external XML file selected by an XPath query and emitted into the generated documentation file. `` remains unsupported. ([Issue #19175](https://github.com/dotnet/fsharp/issues/19175), [PR #19186](https://github.com/dotnet/fsharp/pull/19186)) * Expand `` at tooling time. In IDE tooltips, completion, and signature help, documentation is inherited from base classes, interfaces, overridden members, and constructors (matched by parameter signature). The FCS Symbols API (`FSharpSymbol.XmlDoc`) additionally resolves explicit `cref` targets, but does not expand constructor inheritance. The compiler emits the tag verbatim into generated XML documentation files, matching C#; `` is not implemented. ([Issue #19175](https://github.com/dotnet/fsharp/issues/19175), [PR #19188](https://github.com/dotnet/fsharp/pull/19188)) * Add symbol and type highlighting to F# diagnostics ([PR #20097](https://github.com/dotnet/fsharp/pull/20097)) -* IL: add `ILPreNamespace`, make `ILPreTypeDef` creation lazy ([PR #20092](https://github.com/dotnet/fsharp/pull/20092)) -* IL: use empty tables for members when possible ([PR #20249](https://github.com/dotnet/fsharp/pull/20249)) ### Improved * Nullness warning FS3261 on dotted method or property access (e.g. `x.Member`) now underlines the receiver expression and includes the member name and (when known) the binding name in the message. ([Issue #19658](https://github.com/dotnet/fsharp/issues/19658), [PR #19814](https://github.com/dotnet/fsharp/pull/19814)) * Direct delegate construction ([PR ##19993](https://github.com/dotnet/fsharp/pull/19993)) +* IL: add `ILPreNamespace`, make `ILPreTypeDef` creation lazy ([PR #20092](https://github.com/dotnet/fsharp/pull/20092)) +* IL: share ILCallingConv instances ([PR #20254](https://github.com/dotnet/fsharp/pull/20254) +* IL: use empty tables for members when possible ([PR #20249](https://github.com/dotnet/fsharp/pull/20249)) ### Changed diff --git a/src/Compiler/AbstractIL/il.fs b/src/Compiler/AbstractIL/il.fs index 0aa4e76ecf4..8b7eeb25aee 100644 --- a/src/Compiler/AbstractIL/il.fs +++ b/src/Compiler/AbstractIL/il.fs @@ -683,6 +683,9 @@ type ILCallingConv = static member Static = ILCallingConvStatics.Static + static member Create(thisConv, argConv) = + ILCallingConvStatics.Get(thisConv, argConv) + override x.ToString() = if x.IsStatic then "static" else "instance" @@ -693,10 +696,55 @@ and ILCallingConvStatics() = static let staticCallConv = Callconv(ILThisConvention.Static, ILArgConvention.Default) + /// Every combination, so that reading metadata never allocates a calling convention. The two + /// common ones above are placed in the table too, so all uses share one instance per combination. + static let allCallConvs = + let thisConvs = + [| + ILThisConvention.Instance + ILThisConvention.InstanceExplicit + ILThisConvention.Static + |] + + let argConvs = + [| + ILArgConvention.Default + ILArgConvention.CDecl + ILArgConvention.StdCall + ILArgConvention.ThisCall + ILArgConvention.FastCall + ILArgConvention.VarArg + |] + + Array.init (thisConvs.Length * argConvs.Length) (fun i -> + match thisConvs[i / argConvs.Length], argConvs[i % argConvs.Length] with + | ILThisConvention.Instance, ILArgConvention.Default -> instanceCallConv + | ILThisConvention.Static, ILArgConvention.Default -> staticCallConv + | thisConv, argConv -> Callconv(thisConv, argConv)) + static member Instance = instanceCallConv static member Static = staticCallConv + static member Get(thisConv, argConv) = + // Explicit, so adding a case to either union is a compile error here rather than a bad index. + let thisIdx = + match thisConv with + | ILThisConvention.Instance -> 0 + | ILThisConvention.InstanceExplicit -> 1 + | ILThisConvention.Static -> 2 + + let argIdx = + match argConv with + | ILArgConvention.Default -> 0 + | ILArgConvention.CDecl -> 1 + | ILArgConvention.StdCall -> 2 + | ILArgConvention.ThisCall -> 3 + | ILArgConvention.FastCall -> 4 + | ILArgConvention.VarArg -> 5 + + allCallConvs[thisIdx * 6 + argIdx] + type ILBoxity = | AsObject | AsValue diff --git a/src/Compiler/AbstractIL/il.fsi b/src/Compiler/AbstractIL/il.fsi index 8e82bd176fb..aa2e7c69b2f 100644 --- a/src/Compiler/AbstractIL/il.fsi +++ b/src/Compiler/AbstractIL/il.fsi @@ -175,17 +175,22 @@ type ILThisConvention = [] type ILCallingConv = + private | Callconv of ILThisConvention * ILArgConvention - member internal IsInstance: bool - member internal IsInstanceExplicit: bool - member internal IsStatic: bool - member internal ThisConv: ILThisConvention - member internal BasicConv: ILArgConvention + member IsInstance: bool + member IsInstanceExplicit: bool + member IsStatic: bool + member ThisConv: ILThisConvention + member BasicConv: ILArgConvention static member Instance: ILCallingConv static member Static: ILCallingConv + /// Returns the shared instance for this combination. Since the representation is private and there + /// are only 18 combinations, no calling convention is ever allocated per method signature. + static member Create: ILThisConvention * ILArgConvention -> ILCallingConv + /// Array shapes. For most purposes the rank is the only thing that matters. type internal ILArrayBound = int32 option diff --git a/src/Compiler/AbstractIL/ilpars.fsy b/src/Compiler/AbstractIL/ilpars.fsy index ca06f6570be..1b5417e24be 100644 --- a/src/Compiler/AbstractIL/ilpars.fsy +++ b/src/Compiler/AbstractIL/ilpars.fsy @@ -189,11 +189,11 @@ typSpec: callConv: INSTANCE callKind - { Callconv (ILThisConvention.Instance,$2) } + { ILCallingConv.Create (ILThisConvention.Instance,$2) } | EXPLICIT callKind - { Callconv (ILThisConvention.InstanceExplicit,$2) } + { ILCallingConv.Create (ILThisConvention.InstanceExplicit,$2) } | callKind - { Callconv (ILThisConvention.Static,$1) } + { ILCallingConv.Create (ILThisConvention.Static,$1) } callKind: /* EMPTY */ diff --git a/src/Compiler/AbstractIL/ilprint.fs b/src/Compiler/AbstractIL/ilprint.fs index f1fb38174c9..0a892c76d3d 100644 --- a/src/Compiler/AbstractIL/ilprint.fs +++ b/src/Compiler/AbstractIL/ilprint.fs @@ -365,15 +365,15 @@ and output_bcc os bcc = | ILArgConvention.Default -> " " | ILArgConvention.VarArg -> "vararg ") -and output_callconv os (Callconv(hasthis, cc)) = +and output_callconv os (callconv: ILCallingConv) = output_string os - (match hasthis with + (match callconv.ThisConv with | ILThisConvention.Instance -> "instance " | ILThisConvention.InstanceExplicit -> "explicit " | ILThisConvention.Static -> "") - output_bcc os cc + output_bcc os callconv.BasicConv and goutput_dlocref env os (dref: ILType) = match dref with diff --git a/src/Compiler/AbstractIL/ilread.fs b/src/Compiler/AbstractIL/ilread.fs index bc31548cbdd..d0eae0bc82f 100644 --- a/src/Compiler/AbstractIL/ilread.fs +++ b/src/Compiler/AbstractIL/ilread.fs @@ -2861,7 +2861,7 @@ and byteAsCallConv b = ILArgConvention.Default let generic = (b &&& e_IMAGE_CEE_CS_CALLCONV_GENERIC) <> 0x0uy - generic, Callconv(byteAsHasThis b, cc) + generic, ILCallingConv.Create(byteAsHasThis b, cc) and seekReadMemberRefAsMethodData ctxt numTypars idx : VarArgMethodData = ctxt.seekReadMemberRefAsMethodData (MemberRefAsMspecIdx(numTypars, idx)) diff --git a/src/Compiler/AbstractIL/ilreflect.fs b/src/Compiler/AbstractIL/ilreflect.fs index 9aa9d6403a0..78b1fdf8b67 100644 --- a/src/Compiler/AbstractIL/ilreflect.fs +++ b/src/Compiler/AbstractIL/ilreflect.fs @@ -694,15 +694,15 @@ let envPopEntryPts emEnv = // convCallConv //---------------------------------------------------------------------------- -let convCallConv (Callconv(hasThis, basic)) = +let convCallConv (callConv: ILCallingConv) = let ccA = - match hasThis with + match callConv.ThisConv with | ILThisConvention.Static -> CallingConventions.Standard | ILThisConvention.InstanceExplicit -> CallingConventions.ExplicitThis | ILThisConvention.Instance -> CallingConventions.HasThis let ccB = - match basic with + match callConv.BasicConv with | ILArgConvention.Default -> enum 0 | ILArgConvention.CDecl -> enum 0 | ILArgConvention.StdCall -> enum 0 diff --git a/src/Compiler/AbstractIL/ilwrite.fs b/src/Compiler/AbstractIL/ilwrite.fs index f626e1e56ef..b974e814574 100644 --- a/src/Compiler/AbstractIL/ilwrite.fs +++ b/src/Compiler/AbstractIL/ilwrite.fs @@ -838,10 +838,10 @@ let hasthisToByte hasthis = | ILThisConvention.InstanceExplicit -> e_IMAGE_CEE_CS_CALLCONV_INSTANCE_EXPLICIT | ILThisConvention.Static -> 0x00uy -let callconvToByte ntypars (Callconv (hasthis, bcc)) = - hasthisToByte hasthis ||| +let callconvToByte ntypars (callconv: ILCallingConv) = + hasthisToByte callconv.ThisConv ||| (if ntypars > 0 then e_IMAGE_CEE_CS_CALLCONV_GENERIC else 0x00uy) ||| - (match bcc with + (match callconv.BasicConv with | ILArgConvention.FastCall -> e_IMAGE_CEE_CS_CALLCONV_FASTCALL | ILArgConvention.StdCall -> e_IMAGE_CEE_CS_CALLCONV_STDCALL | ILArgConvention.ThisCall -> e_IMAGE_CEE_CS_CALLCONV_THISCALL diff --git a/src/Compiler/TypedTree/TypedTreePickle.fs b/src/Compiler/TypedTree/TypedTreePickle.fs index 5b64b10f600..1206d7afd7c 100644 --- a/src/Compiler/TypedTree/TypedTreePickle.fs +++ b/src/Compiler/TypedTree/TypedTreePickle.fs @@ -1285,8 +1285,8 @@ and p_ILBasicCallConv x st = | ILArgConvention.VarArg -> 5) st -and p_ILCallConv (Callconv(x, y)) st = - p_tup2 p_ILHasThis p_ILBasicCallConv (x, y) st +and p_ILCallConv (x: ILCallingConv) st = + p_tup2 p_ILHasThis p_ILBasicCallConv (x.ThisConv, x.BasicConv) st and p_ILCallSig x st = p_tup3 p_ILCallConv p_ILTypes p_ILType (x.CallingConv, x.ArgTypes, x.ReturnType) st @@ -1316,7 +1316,7 @@ let u_ILHasThis st = let u_ILCallConv st = let a, b = u_tup2 u_ILHasThis u_ILBasicCallConv st - Callconv(a, b) + ILCallingConv.Create(a, b) let u_ILTypeRef st = let a, b, c = u_tup3 u_ILScopeRef u_strings u_string st diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl index 055808bf332..ba8d1825ece 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl @@ -319,22 +319,26 @@ FSharp.Compiler.AbstractIL.IL+ILCallingConv: Boolean Equals(ILCallingConv) FSharp.Compiler.AbstractIL.IL+ILCallingConv: Boolean Equals(ILCallingConv, System.Collections.IEqualityComparer) FSharp.Compiler.AbstractIL.IL+ILCallingConv: Boolean Equals(System.Object) FSharp.Compiler.AbstractIL.IL+ILCallingConv: Boolean Equals(System.Object, System.Collections.IEqualityComparer) -FSharp.Compiler.AbstractIL.IL+ILCallingConv: ILArgConvention Item2 -FSharp.Compiler.AbstractIL.IL+ILCallingConv: ILArgConvention get_Item2() +FSharp.Compiler.AbstractIL.IL+ILCallingConv: Boolean IsInstance +FSharp.Compiler.AbstractIL.IL+ILCallingConv: Boolean IsInstanceExplicit +FSharp.Compiler.AbstractIL.IL+ILCallingConv: Boolean IsStatic +FSharp.Compiler.AbstractIL.IL+ILCallingConv: Boolean get_IsInstance() +FSharp.Compiler.AbstractIL.IL+ILCallingConv: Boolean get_IsInstanceExplicit() +FSharp.Compiler.AbstractIL.IL+ILCallingConv: Boolean get_IsStatic() +FSharp.Compiler.AbstractIL.IL+ILCallingConv: ILArgConvention BasicConv +FSharp.Compiler.AbstractIL.IL+ILCallingConv: ILArgConvention get_BasicConv() +FSharp.Compiler.AbstractIL.IL+ILCallingConv: ILCallingConv Create(ILThisConvention, ILArgConvention) FSharp.Compiler.AbstractIL.IL+ILCallingConv: ILCallingConv Instance -FSharp.Compiler.AbstractIL.IL+ILCallingConv: ILCallingConv NewCallconv(ILThisConvention, ILArgConvention) FSharp.Compiler.AbstractIL.IL+ILCallingConv: ILCallingConv Static FSharp.Compiler.AbstractIL.IL+ILCallingConv: ILCallingConv get_Instance() FSharp.Compiler.AbstractIL.IL+ILCallingConv: ILCallingConv get_Static() -FSharp.Compiler.AbstractIL.IL+ILCallingConv: ILThisConvention Item1 -FSharp.Compiler.AbstractIL.IL+ILCallingConv: ILThisConvention get_Item1() +FSharp.Compiler.AbstractIL.IL+ILCallingConv: ILThisConvention ThisConv +FSharp.Compiler.AbstractIL.IL+ILCallingConv: ILThisConvention get_ThisConv() FSharp.Compiler.AbstractIL.IL+ILCallingConv: Int32 CompareTo(ILCallingConv) FSharp.Compiler.AbstractIL.IL+ILCallingConv: Int32 CompareTo(System.Object) FSharp.Compiler.AbstractIL.IL+ILCallingConv: Int32 CompareTo(System.Object, System.Collections.IComparer) FSharp.Compiler.AbstractIL.IL+ILCallingConv: Int32 GetHashCode() FSharp.Compiler.AbstractIL.IL+ILCallingConv: Int32 GetHashCode(System.Collections.IEqualityComparer) -FSharp.Compiler.AbstractIL.IL+ILCallingConv: Int32 Tag -FSharp.Compiler.AbstractIL.IL+ILCallingConv: Int32 get_Tag() FSharp.Compiler.AbstractIL.IL+ILCallingConv: System.String ToString() FSharp.Compiler.AbstractIL.IL+ILCallingSignature: Boolean Equals(ILCallingSignature) FSharp.Compiler.AbstractIL.IL+ILCallingSignature: Boolean Equals(ILCallingSignature, System.Collections.IEqualityComparer) diff --git a/tests/FSharp.Compiler.Service.Tests/ModuleReaderCancellationTests.fs b/tests/FSharp.Compiler.Service.Tests/ModuleReaderCancellationTests.fs index 473a503ba13..fc3641811a5 100644 --- a/tests/FSharp.Compiler.Service.Tests/ModuleReaderCancellationTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/ModuleReaderCancellationTests.fs @@ -50,7 +50,7 @@ module ModuleReader = MethodAttributes.NewSlot ||| MethodAttributes.SpecialName - let callingConv = Callconv(ILThisConvention.Instance, ILArgConvention.Default) + let callingConv = ILCallingConv.Instance let parameters = [] let ret = mkILReturn ILType.Void let genericParams = []