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 7dd68964cb3..97589376dc8 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -166,6 +166,7 @@ * 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)) +* IL: hold custom attributes in fields rather than a union case ### Improved diff --git a/src/Compiler/AbstractIL/il.fs b/src/Compiler/AbstractIL/il.fs index 0aa4e76ecf4..4d33e1ff78b 100644 --- a/src/Compiler/AbstractIL/il.fs +++ b/src/Compiler/AbstractIL/il.fs @@ -1262,14 +1262,18 @@ type WellKnownILAttributes = | OverloadResolutionPriorityAttribute = (1u <<< 26) | NotComputed = (1u <<< 31) -type internal ILAttributesStoredRepr = - | Reader of (int32 -> ILAttribute[]) - | Given of ILAttributes - [] -type ILAttributesStored private (metadataIndex: int32, initial: ILAttributesStoredRepr) = +type ILAttributesStored private (metadataIndex: int32, reader: int32 -> ILAttribute[], given: ILAttribute[] | null) = + + /// Stands in for the reader when the attributes are already in hand, so the field can stay non-null. + static let noReader: int32 -> ILAttribute[] = fun _ -> [||] + + // Holds the array rather than an ILAttributesStoredRepr. The reader function is shared per metadata + // reader per attribute table, so it is held directly; wrapping it cost one object per owner, and most + // owners are never forced, so most of those existed only to say "not read yet". ILAttributes is a + // struct over the array, so rewrapping on each read allocates nothing. [] - let mutable repr = initial + let mutable attrArray: ILAttribute[] | null = given [] let mutable wellKnownFlags = WellKnownILAttributes.NotComputed @@ -1277,12 +1281,12 @@ type ILAttributesStored private (metadataIndex: int32, initial: ILAttributesStor member _.MetadataIndex = metadataIndex member x.CustomAttrs: ILAttributes = - match repr with - | Given a -> a - | Reader f -> - let r = ILAttributes(f metadataIndex) - repr <- Given r - r + match attrArray with + | null -> + let a = reader metadataIndex + attrArray <- a + ILAttributes a + | a -> ILAttributes a member x.HasWellKnownAttribute(flag: WellKnownILAttributes, compute: ILAttributes -> WellKnownILAttributes) : bool = x.GetOrComputeWellKnownFlags(compute) &&& flag <> WellKnownILAttributes.None @@ -1298,9 +1302,10 @@ type ILAttributesStored private (metadataIndex: int32, initial: ILAttributesStor wellKnownFlags <- computed computed - static member CreateReader(idx: int32, f: int32 -> ILAttribute[]) = ILAttributesStored(idx, Reader f) + static member CreateReader(idx: int32, f: int32 -> ILAttribute[]) = ILAttributesStored(idx, f, null) - static member CreateGiven(attrs: ILAttributes) = ILAttributesStored(-1, Given attrs) + static member CreateGiven(attrs: ILAttributes) = + ILAttributesStored(-1, noReader, attrs.AsArray()) let emptyILCustomAttrs = ILAttributes [||]