Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Nest augmentation-member closures inside their declaring type under --realsig+#19955

Open
T-Gro wants to merge 6 commits into
mainfrom
fix/realsig-class-private-member-closure
Open

Nest augmentation-member closures inside their declaring type under --realsig+#19955
T-Gro wants to merge 6 commits into
mainfrom
fix/realsig-class-private-member-closure

Conversation

@T-Gro

@T-Gro T-Gro commented Jun 16, 2026

Copy link
Copy Markdown
Member

Fixes #19933

A closure synthesized inside a member declared in an intrinsic augmentation
(type C with member ...) was emitted as a sibling of C in the enclosing
module class rather than nested inside C. Under --realsig+ a source-private
member of C is IL private (type-scoped), so the sibling closure could not
reach it and the CLR raised MethodAccessException at first invocation. Members
declared in the type's own body were always nested correctly.

type C() =
    static member private Secret() = 1   // IL `private` under --realsig+
type C with
    member _.Run() =
        let rec h n = if n = 0 then C.Secret() else h (n - 1)
        h 5
// before: closure `h` emitted beside C -> MethodAccessException at runtime
// after:  closure `h` nested inside C  -> runs

The same placement also affected task/async state machines and
quotation-splice helpers in augmentation members. Access semantics are unchanged
— the member stays IL private; only the compiler-synthesized closure is
re-homed into the declaring type, matching how members declared in the type body
already compile. The change is scoped to --realsig+.

…-realsig+

A closure synthesized inside a member declared in an intrinsic augmentation
(`type C with member ...`) was emitted as a sibling of `C` in the enclosing
module class instead of nested inside `C`. Under --realsig+ a source-private
member of `C` is IL `private` (type-scoped), so the sibling closure could not
reach it and the CLR raised MethodAccessException at first invocation. Members
declared in the type's own body were always nested correctly.

GenMethodForBinding now normalizes eenv.cloc to the member's declaring type for
all non-extension members under --realsig+, so the closure nests inside the type
(idempotent for members that already had it). The program semantics are unchanged
and the private member stays IL `private`.

Fixes #19933

Co-authored-by: Copilot <[email protected]>
@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

❗ Release notes required

You can open this PR in browser to add release notes: open in github.dev


✅ Found changes and release notes in following paths:

Change path Release notes path Description
src/Compiler docs/release-notes/.FSharp.Compiler.Service/11.0.100.md

T-Gro and others added 2 commits June 16, 2026 15:11
…en skill

The skill-and-agent validator (triggered by adding a skill) requires every agent
frontmatter to declare a name; agentic-workflows.agent.md was missing it.

Co-authored-by: Copilot <[email protected]>
@auduchinok

auduchinok commented Jun 16, 2026

Copy link
Copy Markdown
Member

A possibly related issue: #5302. Maybe this kind of closures could be fixed with these changes as well.

… shapes

Adversarial review (5 models) found the original tests under-powered: runtime-only
(no IL-nesting assertion, so they passed even on the buggy compiler under realsig-),
the instance-member case used an inlinable private that hid the bug, and most member
kinds were uncovered.

- Add Regression_RealsigAugmentationClosure_StructuralAssertions.fs: under --realsig+
  assert the closure nests under the declaring type (verifyILPresent "Type/closure@")
  and is not a module sibling (verifyILNotPresent). These FAIL on the pre-fix compiler
  and PASS on the fix, so they actually guard the IL shape.
- Mark every private member [<NoCompilerInlining>] so the call survives the optimizer
  (the previous instance test guarded nothing).
- Cover property getter/setter, indexer, static method, operator, seq, mutual rec,
  nested closures, generic method (typar threading), two-type closure-name collision,
  record/DU, nested-module and namespace-scoped types.

Co-authored-by: Copilot <[email protected]>
@github-actions github-actions Bot added the AI-Tooling-Check-Bypassed Tooling check: non-fork PR, not diff-analyzed label Jun 16, 2026
…lity review)

A 5-model test-quality review found the prior tests duplicated source across two
files, used a vacuous --realsig- matrix, and reinvented framework helpers.

- Merge runtime + structural files into one MemberData-driven Theory: each shape is
  asserted structurally (closure nests under the declaring type, not a module sibling)
  AND run. 23 shapes as data rows + 1 --realsig- smoke (defense-in-depth only; the fix
  is gated on realsig+, so realsig- IL is identical before/after).
- Add 3 shapes the review found uncovered and confirmed crash-on-shipped/fixed-on-PR:
  override member, secondary constructor, struct augmentation.
- Harden the two-type and async absent-fragments against the closure-name
  disambiguator ('h@N-1').
- Add an AugmentationClosureNesting.fs FileInlineData baseline (Realsig=Both) in
  Inlining.fs (idiomatic EmittedIL lock) snapshotting both the realsig+ nesting
  (closure nested in C, member IL private) and the realsig- lowering.

Co-authored-by: Copilot <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI-Tooling-Check-Bypassed Tooling check: non-fork PR, not diff-analyzed

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

MethodAccessException for type-private member call from inner-rec in a type C with member ... augmentation under --realsig+

2 participants