Skip to content

[Repo Assist] perf/refactor: unify canBind* helpers; skip Array.filter on bindAll path#509

Draft
github-actions[bot] wants to merge 2 commits intomasterfrom
repo-assist/perf-canBind-helpers-20260425-49d046a5ffb3a8de
Draft

[Repo Assist] perf/refactor: unify canBind* helpers; skip Array.filter on bindAll path#509
github-actions[bot] wants to merge 2 commits intomasterfrom
repo-assist/perf-canBind-helpers-20260425-49d046a5ffb3a8de

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

🤖 This is an automated PR from Repo Assist.

Summary

Two closely-related improvements to ProvidedTypes.fs (Tasks 8 & 5):

Task 5 — Coding improvement: unify canBind* helpers

The five visibility-filter helpers (canBindConstructor, canBindMethod, canBindProperty, canBindField, canBindEvent) had identical bodies:

hasFlag bindingFlags BindingFlags.Public && c.IsPublic
|| hasFlag bindingFlags BindingFlags.NonPublic && not c.IsPublic

These are now expressed via a shared canBindByVisibility inline helper, reducing duplication and making the pattern more explicit.

Task 8 — Performance improvement: skip Array.filter on the bindAll path

In TargetTypeDefinition.GetMethods(bindingFlags) (and GetConstructors, GetFields, GetEvents, GetProperties, GetNestedTypes), every call previously did:

methDefs.Force() |> Array.filter (canBindMethod bindingFlags)

methDefs.Force() is cached (Lazy), but Array.filter allocates a new array on every call. When both Public and NonPublic are in bindingFlags every member passes the filter — the allocation is wasted.

The new isVisibilityBindAll helper detects this case and returns the cached array directly:

let arr = methDefs.Force()
if isVisibilityBindAll bindingFlags then arr else arr |> Array.filter (canBindMethod bindingFlags)
```

The F# compiler uses `bindAll = DeclaredOnly ||| Public ||| NonPublic ||| Static ||| Instance` for all member-enumeration calls, so this eliminates a redundant O(n) allocation on every `Get*` call against every `TargetTypeDefinition` (the target-assembly-backed type representation used by all generative type providers).

The same optimisation is applied to `TypeSymbol.Get*` for `TargetGeneric` instantiations.

## Test Status

```
Passed! - Failed: 0, Passed: 157, Skipped: 0, Total: 157

All 157 tests pass with no changes to test code.

Generated by 🌈 Repo Assist, see workflow run. Learn more.

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@96b9d4c39aa22359c0b38265927eadb31dcf4e2a

…ath in TargetTypeDefinition and TypeSymbol

- Extract canBindByVisibility inline helper shared by all five canBind* functions
  (canBindConstructor / canBindMethod / canBindProperty / canBindField / canBindEvent),
  removing five nearly-identical bodies.
- Add isVisibilityBindAll helper: returns true when both Public and NonPublic
  BindingFlags are set, meaning every member passes the visibility filter.
- Use isVisibilityBindAll in TargetTypeDefinition.GetConstructors/GetMethods/
  GetFields/GetEvents/GetProperties/GetNestedTypes: return the already-computed
  lazy array directly instead of calling Array.filter and allocating a fresh copy.
  The F# compiler always calls these with bindAll (Public|NonPublic|Static|Instance|
  DeclaredOnly), so this eliminates a redundant O(n) allocation on every member
  enumeration of every TargetTypeDefinition.
- Apply the same short-circuit in TypeSymbol.Get* for TargetGeneric instantiations.

157/157 tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants