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 0c576e47667..6c44962dfae 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -174,6 +174,7 @@ * 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)) +* Make Entity's adhoc members list lazy ([PR #20286](https://github.com/dotnet/fsharp/pull/20286/changes)) ### Changed * The `--warnaserror` option now ignores unrecognized diagnostic identifiers in warning lists while still applying recognized F# warning codes. ([PR #20246](https://github.com/dotnet/fsharp/pull/20246)) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 03ea69f01c2..f8d265eea4c 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -1200,7 +1200,7 @@ let PublishValueDefnMaybeInclCompilerGenerated (cenv: cenv) env inclCompilerGene let tcaug = vspec.MemberApparentEntity.TypeContents let vref = mkLocalValRef vspec tcaug.tcaug_adhoc <- NameMultiMap.add vspec.LogicalName vref tcaug.tcaug_adhoc - tcaug.tcaug_adhoc_list.Add (ValRefIsExplicitImpl g vref, vref) + tcaug.AddAdhocMember(ValRefIsExplicitImpl g vref, vref) | _ -> () let PublishValueDefn cenv env declKind vspec = diff --git a/src/Compiler/TypedTree/TypedTree.fs b/src/Compiler/TypedTree/TypedTree.fs index 44e17e3b667..bfb711ee07d 100644 --- a/src/Compiler/TypedTree/TypedTree.fs +++ b/src/Compiler/TypedTree/TypedTree.fs @@ -704,7 +704,7 @@ type Entity = /// Used during codegen to hold the ILX representation indicating how to access the type // MUTABILITY: only for unpickle linkage and caching - mutable entity_il_repr_cache: CompiledTypeRepr cache + mutable entity_il_repr_cache: CompiledTypeRepr cache | null mutable entity_opt_data: EntityOptionalData option } @@ -949,7 +949,15 @@ type Entity = | _ -> TAccess [] /// Get the cache of the compiled ILTypeRef representation of this module or type. - member x.CompiledReprCache = x.entity_il_repr_cache + /// Allocated on first request rather than per entity: only codegen asks for it, so an analysis-only + /// host never pays for one. + member x.CompiledReprCache = + match x.entity_il_repr_cache with + | null -> + let c = newCache () + x.entity_il_repr_cache <- c + c + | c -> c /// Get a blob of data indicating how this type is nested in other namespaces, modules or types. member x.PublicPath = x.entity_pubpath @@ -1481,8 +1489,12 @@ type TyconAugmentation = /// Properties, methods etc. in declaration order. The boolean flag for each indicates if the /// member is known to be an explicit interface implementation. This must be computed and /// saved prior to remapping assembly information. - tcaug_adhoc_list: ResizeArray - + // + // Allocated on first member: there is one augmentation per entity, and nothing is ever added for + // an imported type. + mutable tcaug_adhoc_list: ResizeArray | null + + /// Properties, methods etc. as lookup table mutable tcaug_adhoc: NameMultiMap @@ -1499,6 +1511,24 @@ type TyconAugmentation = mutable tcaug_abstract: bool } + /// Record a member in declaration order, allocating the list on first use + member tcaug.AddAdhocMember(isExplicitImpl: bool, vref: ValRef) = + let list = + match tcaug.tcaug_adhoc_list with + | null -> + let l = ResizeArray() + tcaug.tcaug_adhoc_list <- l + l + | l -> l + + list.Add(isExplicitImpl, vref) + + /// Members in declaration order, empty when the type has none + member tcaug.AdhocMembers: (bool * ValRef) list = + match tcaug.tcaug_adhoc_list with + | null -> [] + | l -> List.ofSeq l + member tcaug.SetCompare x = tcaug.tcaug_compare <- Some x member tcaug.SetCompareWith x = tcaug.tcaug_compare_withc <- Some x @@ -1516,7 +1546,7 @@ type TyconAugmentation = tcaug_hash_and_equals_withc=None tcaug_hasObjectGetHashCode=false tcaug_adhoc=NameMultiMap.empty - tcaug_adhoc_list=ResizeArray<_>() + tcaug_adhoc_list=null tcaug_super=None tcaug_interfaces=[] tcaug_closed=false @@ -6313,7 +6343,7 @@ type Construct() = // Generated types get internal accessibility entity_pubpath = Some pubpath entity_cpath = Some cpath - entity_il_repr_cache = newCache() + entity_il_repr_cache = null entity_opt_data = match kind, access with | TyparKind.Type, TAccess [] -> None @@ -6338,7 +6368,7 @@ type Construct() = entity_pubpath=cpath |> Option.map (fun (cp: CompilationPath) -> cp.NestedPublicPath id) entity_cpath=cpath entity_attribs=WellKnownEntityAttribs.Create(attribs) - entity_il_repr_cache = newCache() + entity_il_repr_cache = null entity_opt_data = match xml, access with | doc, TAccess [] when doc.IsEmpty -> None @@ -6416,7 +6446,7 @@ type Construct() = entity_typars = LazyWithContext.NotLazy [] entity_tycon_repr = TNoRepr entity_flags = EntityFlags(usesPrefixDisplay=false, isModuleOrNamespace=false, preEstablishedHasDefaultCtor=false, hasSelfReferentialCtor=false, isStructRecordOrUnionType=false) - entity_il_repr_cache = newCache() + entity_il_repr_cache = null entity_opt_data = match doc, access, repr with | doc, TAccess [], TExnNone when doc.IsEmpty -> None @@ -6455,7 +6485,7 @@ type Construct() = entity_modul_type = mtyp entity_pubpath=cpath |> Option.map (fun (cp: CompilationPath) -> cp.NestedPublicPath (mkSynId m nm)) entity_cpath = cpath - entity_il_repr_cache = newCache() + entity_il_repr_cache = null entity_opt_data = match kind, doc, reprAccess, access with | TyparKind.Type, doc, TAccess [], TAccess [] when doc.IsEmpty -> None diff --git a/src/Compiler/TypedTree/TypedTree.fsi b/src/Compiler/TypedTree/TypedTree.fsi index dede50853d2..787d7b47bbf 100644 --- a/src/Compiler/TypedTree/TypedTree.fsi +++ b/src/Compiler/TypedTree/TypedTree.fsi @@ -455,7 +455,7 @@ type Entity = mutable entity_cpath: CompilationPath option /// Used during codegen to hold the ILX representation indicating how to access the type - mutable entity_il_repr_cache: cache + mutable entity_il_repr_cache: cache | null mutable entity_opt_data: EntityOptionalData option } @@ -891,7 +891,7 @@ type TyconAugmentation = /// Properties, methods etc. in declaration order. The boolean flag for each indicates if the /// member is known to be an explicit interface implementation. This must be computed and /// saved prior to remapping assembly information. - tcaug_adhoc_list: ResizeArray + mutable tcaug_adhoc_list: ResizeArray | null /// Properties, methods etc. as lookup table mutable tcaug_adhoc: NameMultiMap @@ -911,6 +911,12 @@ type TyconAugmentation = static member Create: unit -> TyconAugmentation + /// Record a member in declaration order, allocating the list on first use + member AddAdhocMember: isExplicitImpl: bool * vref: ValRef -> unit + + /// Members in declaration order, empty when the type has none + member AdhocMembers: (bool * ValRef) list + member SetCompare: x: (ValRef * ValRef) -> unit member SetCompareWith: x: ValRef -> unit diff --git a/src/Compiler/TypedTree/TypedTreeOps.Remapping.fs b/src/Compiler/TypedTree/TypedTreeOps.Remapping.fs index 516c1010bec..9becdadaa54 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.Remapping.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.Remapping.fs @@ -2182,8 +2182,12 @@ module internal ExprRemapping = |> Option.map (mapQuadruple (remapValRef tmenv, remapValRef tmenv, remapValRef tmenv, Option.map (remapValRef tmenv))) tcaug_adhoc = x.tcaug_adhoc |> NameMap.map (List.map (remapValRef tmenv)) tcaug_adhoc_list = - x.tcaug_adhoc_list - |> ResizeArray.map (fun (flag, vref) -> (flag, remapValRef tmenv vref)) + let remapped: ResizeArray | null = + match x.tcaug_adhoc_list with + | null -> null + | l -> l |> ResizeArray.map (fun (flag, vref) -> (flag, remapValRef tmenv vref)) + + remapped tcaug_super = x.tcaug_super |> Option.map (remapType tmenv) tcaug_interfaces = x.tcaug_interfaces |> List.map (map1Of3 (remapType tmenv)) } diff --git a/src/Compiler/TypedTree/TypedTreePickle.fs b/src/Compiler/TypedTree/TypedTreePickle.fs index 9adfca4e3a8..53b4b9668ca 100644 --- a/src/Compiler/TypedTree/TypedTreePickle.fs +++ b/src/Compiler/TypedTree/TypedTreePickle.fs @@ -2839,8 +2839,7 @@ and p_tcaug p st = p.tcaug_hash_and_equals_withc |> Option.map (fun (v1, v2, v3, _) -> (v1, v2, v3)), p.tcaug_equals, - (p.tcaug_adhoc_list - |> ResizeArray.toList + (p.AdhocMembers // Explicit impls of interfaces only get kept in the adhoc list // in order to get check the well-formedness of an interface. // Keeping them across assembly boundaries is not valid, because relinking their ValRefs @@ -3201,7 +3200,10 @@ and u_tcaug st = tcaug_equals = b2 // only used for code generation and checking - hence don't care about the values when reading back in tcaug_hasObjectGetHashCode = false - tcaug_adhoc_list = ResizeArray<_>(c |> List.map (fun (_, vref) -> (false, vref))) + tcaug_adhoc_list = + match c with + | [] -> null + | _ -> ResizeArray<_>(c |> List.map (fun (_, vref) -> (false, vref))) tcaug_adhoc = NameMultiMap.ofList c tcaug_interfaces = d tcaug_super = e