Skip to content

Commit d2baca0

Browse files
[Repo Assist] Perf: cache FullName, BaseType and GetInterfaces in TargetTypeDefinition (#485)
🤖 *This is an automated PR from Repo Assist, an AI assistant for this repository.* ## Summary `TargetTypeDefinition.FullName`, `BaseType`, and `GetInterfaces()` each compute their result from immutable input data (`inp.Namespace`/`inp.Name`, `inp.Extends`, `inp.Implements`) but were recomputed on every call — allocating new strings/arrays and re-running type resolution each time. For large type providers with many types (e.g. SwaggerProvider), where the F# compiler queries these properties many times per type during type-checking, this saves repeated allocations and type-resolution work. ### Changes | Property | Before | After | |---|---|---| | `FullName` | String concatenation every call | Cached `lazy` — computed once, same `string` returned thereafter | | `BaseType` | `txILType` (type-resolution) every call | Cached `lazy` — resolved once | | `GetInterfaces()` | `Array.map txILType` (allocates new `Type[]`) every call | Cached `lazy` — resolved and allocated once | All three caches use F# `lazy` which defaults to `LazyThreadSafetyMode.ExecutionAndPublication`, so concurrent first-access from multiple F# compiler threads is safe. This is complementary to PR #471 (which cached member-wrapper arrays) and does not touch the thread-safety areas being addressed by PRs #482/#483. ## Test Status ``` Passed! - Failed: 0, Passed: 117, Skipped: 0, Total: 117 (net8.0) ``` All 117 pre-existing tests pass. The `netstandard2.0` build target ran OOM on the CI machine (infrastructure issue, not caused by this change — the same issue affects master); the `net8.0` build and tests both pass cleanly. > Generated by [Repo Assist](https://github.com/fsprojects/FSharp.TypeProviders.SDK/actions/runs/23367946628) · [◷](https://github.com/search?q=repo%3Afsprojects%2FFSharp.TypeProviders.SDK+%22gh-aw-workflow-id%3A+repo-assist%22&type=pullrequests) > > To install this [agentic workflow](https://github.com/githubnext/agentics/tree/d1d884596e62351dd652ae78465885dd32f0dd7d/workflows/repo-assist.md), run > ``` > gh aw add githubnext/agentics@d1d8845 > ``` <!-- gh-aw-agentic-workflow: Repo Assist, engine: copilot, id: 23367946628, workflow_id: repo-assist, run: https://github.com/fsprojects/FSharp.TypeProviders.SDK/actions/runs/23367946628 --> <!-- gh-aw-workflow-id: repo-assist --> --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent dffd529 commit d2baca0

1 file changed

Lines changed: 20 additions & 10 deletions

File tree

src/ProvidedTypes.fs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7982,25 +7982,35 @@ namespace ProviderImplementation.ProvidedTypes
79827982
let propDefs = lazy (inp.Properties.Entries |> Array.map (txILPropertyDef this))
79837983
let nestedDefs = lazy (inp.NestedTypes.Entries |> Array.map (asm.TxILTypeDef (Some (this :> Type))))
79847984

7985+
// Cache derived properties that are computed from immutable input data.
7986+
// The F# compiler may call FullName, BaseType and GetInterfaces() many times per type
7987+
// during type-checking; caching avoids repeated string allocations and type-resolution work.
7988+
let fullName =
7989+
lazy (
7990+
match declTyOpt with
7991+
| None ->
7992+
match inp.Namespace with
7993+
| UNone -> inp.Name
7994+
| USome nsp -> nsp + "." + inp.Name
7995+
| Some declTy ->
7996+
declTy.FullName + "+" + inp.Name)
7997+
7998+
let baseType = lazy (inp.Extends |> Option.map (txILType (gps, [| |])) |> Option.toObj)
7999+
8000+
let interfaces = lazy (inp.Implements |> Array.map (txILType (gps, [| |])))
8001+
79858002
do this.typeImpl <- this
79868003
override __.Name = inp.Name
79878004
override __.Assembly = (asm :> Assembly)
79888005
override __.DeclaringType = declTyOpt |> Option.toObj
79898006
override __.MemberType = if isNested then MemberTypes.NestedType else MemberTypes.TypeInfo
79908007
override __.MetadataToken = inp.Token
79918008

7992-
override __.FullName =
7993-
match declTyOpt with
7994-
| None ->
7995-
match inp.Namespace with
7996-
| UNone -> inp.Name
7997-
| USome nsp -> nsp + "." + inp.Name
7998-
| Some declTy ->
7999-
declTy.FullName + "+" + inp.Name
8009+
override __.FullName = fullName.Value
80008010

80018011
override __.Namespace = inp.Namespace |> StructOption.toObj
8002-
override __.BaseType = inp.Extends |> Option.map (txILType (gps, [| |])) |> Option.toObj
8003-
override __.GetInterfaces() = inp.Implements |> Array.map (txILType (gps, [| |]))
8012+
override __.BaseType = baseType.Value
8013+
override __.GetInterfaces() = interfaces.Value
80048014

80058015
override __.GetConstructors(bindingFlags) =
80068016
ctorDefs.Force() |> Array.filter (canBindConstructor bindingFlags)

0 commit comments

Comments
 (0)