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 38dd2d65b0a..313e6a340e6 100644
--- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md
+++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md
@@ -163,6 +163,7 @@
* 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
diff --git a/src/Compiler/AbstractIL/ilread.fs b/src/Compiler/AbstractIL/ilread.fs
index 0ccdd9cf35c..e764d51a691 100644
--- a/src/Compiler/AbstractIL/ilread.fs
+++ b/src/Compiler/AbstractIL/ilread.fs
@@ -1542,6 +1542,26 @@ let seekReadMethodImplRow (ctxt: ILMetadataReader) mdv idx =
let mdeclIdx = seekReadMethodDefOrRefIdx ctxt mdv &addr
(tidx, mbodyIdx, mdeclIdx)
+/// Rows of a table keyed by type def in its first column: at most one per type in EventMap and
+/// PropertyMap, pointing at its member range, and the overrides themselves in MethodImpl. Called per
+/// type def rather than when the table it feeds is forced, so the types with no rows - most of them -
+/// can share the empty table.
+let seekReadRowRangeForTypeDef (ctxt: ILMetadataReader) mdv (table: TableName) tidx =
+ let searcher =
+ { new ISeekReadIndexedRowReader with
+ member _.GetRow(i, rowIndex) = rowIndex <- i
+ member _.GetKey(rowIndex) = rowIndex
+
+ member _.CompareKey(rowIndex) =
+ let mutable addr = ctxt.rowAddr table rowIndex
+ let rowTidx = seekReadUntaggedIdx TableNames.TypeDef ctxt mdv &addr
+ simpleIndexCompare tidx rowTidx
+
+ member _.ConvertRow(rowIndex) = rowIndex
+ }
+
+ seekReadIndexedRowsRange (ctxt.getNumRows table) true searcher
+
/// Read Table ILModuleRef.
let seekReadModuleRefRow (ctxt: ILMetadataReader) mdv idx =
let mutable addr = ctxt.rowAddr TableNames.ModuleRef idx
@@ -2192,9 +2212,9 @@ and typeDefReader ctxtH : ILTypeDefStored =
let impls = seekReadInterfaceImpls ctxt mdv numTypars idx
- let mimpls = seekReadMethodImpls ctxt numTypars idx
- let props = seekReadProperties ctxt numTypars idx
- let events = seekReadEvents ctxt numTypars idx
+ let mimpls = seekReadMethodImpls ctxt mdv numTypars idx
+ let props = seekReadProperties ctxt mdv numTypars idx
+ let events = seekReadEvents ctxt mdv numTypars idx
ILTypeDef(
name = nm,
@@ -2544,26 +2564,30 @@ and seekReadField ctxt mdv (numTypars, hasLayout) (idx: int) =
)
and seekReadFields (ctxt: ILMetadataReader) (numTypars, hasLayout) fidx1 fidx2 =
- mkILFieldsLazy (
- InterruptibleLazy(fun _ ->
- let mdv = ctxt.mdfile.GetView()
+ if fidx1 <= 0 || fidx2 <= fidx1 then
+ emptyILFields
+ else
+ mkILFieldsLazy (
+ InterruptibleLazy(fun _ ->
+ let mdv = ctxt.mdfile.GetView()
- [
- if fidx1 > 0 then
+ [
for i = fidx1 to fidx2 - 1 do
yield seekReadField ctxt mdv (numTypars, hasLayout) i
- ])
- )
+ ])
+ )
and seekReadMethods (ctxt: ILMetadataReader) numTypars midx1 midx2 =
- mkILMethodsComputed (fun () ->
- let mdv = ctxt.mdfile.GetView()
+ if midx1 <= 0 || midx2 <= midx1 then
+ emptyILMethods
+ else
+ mkILMethodsComputed (fun () ->
+ let mdv = ctxt.mdfile.GetView()
- [|
- if midx1 > 0 then
+ [|
for i = midx1 to midx2 - 1 do
yield seekReadMethod ctxt mdv numTypars i
- |])
+ |])
and sigptrGetTypeDefOrRefOrSpecIdx bytes sigptr =
let struct (n, sigptr) = sigptrGetZInt32 bytes sigptr
@@ -3110,40 +3134,37 @@ and seekReadParamExtras (ctxt: ILMetadataReader) mdv (retRes: byref, p
MetadataIndex = idx
}
-and seekReadMethodImpls (ctxt: ILMetadataReader) numTypars tidx =
- mkILMethodImplsLazy (
- lazy
- let mdv = ctxt.mdfile.GetView()
+and seekReadMethodImpls (ctxt: ILMetadataReader) mdv numTypars tidx =
+ let startIdx, endIdx =
+ seekReadRowRangeForTypeDef ctxt mdv TableNames.MethodImpl tidx
- let mimpls =
- seekReadIndexedRows (
- ctxt.getNumRows TableNames.MethodImpl,
- id,
- id,
- (fun i ->
- let mutable addr = ctxt.rowAddr TableNames.MethodImpl i
- let _tidx = seekReadUntaggedIdx TableNames.TypeDef ctxt mdv &addr
- simpleIndexCompare tidx _tidx),
- isSorted ctxt TableNames.MethodImpl,
- seekReadMethodImplRow ctxt mdv
- )
+ if startIdx <= 0 || endIdx < startIdx then
+ emptyILMethodImpls
+ else
+ mkILMethodImplsLazy (
+ lazy
+ let mdv = ctxt.mdfile.GetView()
- mimpls
- |> List.map (fun (_, b, c) ->
- {
- OverrideBy =
- let (MethodData(enclTy, cc, nm, argTys, retTy, methInst)) =
- seekReadMethodDefOrRefNoVarargs ctxt numTypars b
+ [
+ for i in startIdx..endIdx do
+ let _, b, c = seekReadMethodImplRow ctxt mdv i
+
+ yield
+ {
+ OverrideBy =
+ let (MethodData(enclTy, cc, nm, argTys, retTy, methInst)) =
+ seekReadMethodDefOrRefNoVarargs ctxt numTypars b
- mkILMethSpecInTy (enclTy, cc, nm, argTys, retTy, methInst)
- Overrides =
- let (MethodData(enclTy, cc, nm, argTys, retTy, methInst)) =
- seekReadMethodDefOrRefNoVarargs ctxt numTypars c
+ mkILMethSpecInTy (enclTy, cc, nm, argTys, retTy, methInst)
+ Overrides =
+ let (MethodData(enclTy, cc, nm, argTys, retTy, methInst)) =
+ seekReadMethodDefOrRefNoVarargs ctxt numTypars c
- let mspec = mkILMethSpecInTy (enclTy, cc, nm, argTys, retTy, methInst)
- OverridesSpec(mspec.MethodRef, mspec.DeclaringType)
- })
- )
+ let mspec = mkILMethSpecInTy (enclTy, cc, nm, argTys, retTy, methInst)
+ OverridesSpec(mspec.MethodRef, mspec.DeclaringType)
+ }
+ ]
+ )
and seekReadMultipleMethodSemantics (ctxt: ILMetadataReader) (flags, id) =
seekReadIndexedRows (
@@ -3189,27 +3210,17 @@ and seekReadEvent ctxt mdv numTypars idx =
metadataIndex = idx
)
-(* REVIEW: can substantially reduce numbers of EventMap and PropertyMap reads by first checking if the whole table mdv sorted according to ILTypeDef tokens and then doing a binary chop *)
-and seekReadEvents (ctxt: ILMetadataReader) numTypars tidx =
- mkILEventsLazy (
- InterruptibleLazy(fun _ ->
- let mdv = ctxt.mdfile.GetView()
+and seekReadEvents (ctxt: ILMetadataReader) mdv numTypars tidx =
+ let rowNum, _ = seekReadRowRangeForTypeDef ctxt mdv TableNames.EventMap tidx
+
+ if rowNum <= 0 then
+ emptyILEvents
+ else
+ mkILEventsLazy (
+ InterruptibleLazy(fun _ ->
+ let mdv = ctxt.mdfile.GetView()
+ let _, beginEventIdx = seekReadEventMapRow ctxt mdv rowNum
- match
- seekReadOptionalIndexedRow (
- ctxt.getNumRows TableNames.EventMap,
- id,
- id,
- (fun i ->
- let mutable addr = ctxt.rowAddr TableNames.EventMap i
- let _tidx = seekReadUntaggedIdx TableNames.TypeDef ctxt mdv &addr
- simpleIndexCompare tidx _tidx),
- false,
- (fun i -> i, seekReadEventMapRow ctxt mdv i |> snd)
- )
- with
- | None -> []
- | Some(rowNum, beginEventIdx) ->
let endEventIdx =
if rowNum >= ctxt.getNumRows TableNames.EventMap then
ctxt.getNumRows TableNames.Event + 1
@@ -3222,7 +3233,7 @@ and seekReadEvents (ctxt: ILMetadataReader) numTypars tidx =
for i in beginEventIdx .. endEventIdx - 1 do
yield seekReadEvent ctxt mdv numTypars i
])
- )
+ )
and seekReadProperty ctxt mdv numTypars idx =
let flags, nameIdx, typIdx = seekReadPropertyRow ctxt mdv idx
@@ -3260,26 +3271,17 @@ and seekReadProperty ctxt mdv numTypars idx =
metadataIndex = idx
)
-and seekReadProperties (ctxt: ILMetadataReader) numTypars tidx =
- mkILPropertiesLazy (
- InterruptibleLazy(fun _ ->
- let mdv = ctxt.mdfile.GetView()
+and seekReadProperties (ctxt: ILMetadataReader) mdv numTypars tidx =
+ let rowNum, _ = seekReadRowRangeForTypeDef ctxt mdv TableNames.PropertyMap tidx
+
+ if rowNum <= 0 then
+ emptyILProperties
+ else
+ mkILPropertiesLazy (
+ InterruptibleLazy(fun _ ->
+ let mdv = ctxt.mdfile.GetView()
+ let _, beginPropIdx = seekReadPropertyMapRow ctxt mdv rowNum
- match
- seekReadOptionalIndexedRow (
- ctxt.getNumRows TableNames.PropertyMap,
- id,
- id,
- (fun i ->
- let mutable addr = ctxt.rowAddr TableNames.PropertyMap i
- let _tidx = seekReadUntaggedIdx TableNames.TypeDef ctxt mdv &addr
- simpleIndexCompare tidx _tidx),
- false,
- (fun i -> i, seekReadPropertyMapRow ctxt mdv i |> snd)
- )
- with
- | None -> []
- | Some(rowNum, beginPropIdx) ->
let endPropIdx =
if rowNum >= ctxt.getNumRows TableNames.PropertyMap then
ctxt.getNumRows TableNames.Property + 1
@@ -3292,7 +3294,7 @@ and seekReadProperties (ctxt: ILMetadataReader) numTypars tidx =
for i in beginPropIdx .. endPropIdx - 1 do
yield seekReadProperty ctxt mdv numTypars i
])
- )
+ )
and customAttrsReaderFn ctxtH tag : int32 -> ILAttribute[] =
fun idx ->