Skip to content

Implement direct delegates - #19993

Merged
T-Gro merged 39 commits into
dotnet:mainfrom
kerams:del
Aug 4, 2026
Merged

Implement direct delegates#19993
T-Gro merged 39 commits into
dotnet:mainfrom
kerams:del

Conversation

@kerams

@kerams kerams commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Description

Implements fsharp/fslang-suggestions#1083, fixes #11898. RFC.

Test cases should cover everything mentioned in the language suggestion.

Checklist

  • Test cases added
  • Performance benchmarks added in case of performance changes
  • Release notes entry updated:

@github-actions

github-actions Bot commented Jun 24, 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
LanguageFeatures.fsi docs/release-notes/.Language/preview.md

Comment thread src/Compiler/CodeGen/IlxGen.fs
@github-actions github-actions Bot added the ⚠️ Affects-Compiler-Output Tooling check: PR touches IL emission or codegen label Jun 24, 2026
@github-actions

This comment has been minimized.

Comment thread tests/FSharp.Compiler.ComponentTests/EmittedIL/DirectDelegates/DirectDelegates.fs Outdated
Comment thread src/Compiler/CodeGen/IlxGen.fs
Comment thread src/Compiler/CodeGen/IlxGen.fs Outdated
Comment thread tests/FSharp.Compiler.ComponentTests/EmittedIL/DirectDelegates/DirectDelegates.fs Outdated
Comment thread tests/FSharp.Compiler.ComponentTests/EmittedIL/DirectDelegates/DirectDelegates.fs Outdated
@kerams

kerams commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

Ready for another pass, think I addressed everything. (But alternatives and unresolved questions in the RFC need answers still:))

@smoothdeveloper

Copy link
Copy Markdown
Contributor

Add an opt-out compiler flag (default on) alongside the language feature.

I think this is not needed anymore, Tomas has added a msbuild way as well as compiler flag to disable specific language features:

--disableLanguageFeature:DirectDelegateConstruction

or

 <ItemGroup>
  <DisabledLanguageFeatures Include="DirectDelegateConstruction" />
</ItemGroup>

You may want to check it picks up as is to allow selectively disabling the feature.

@T-Gro

T-Gro commented Jul 23, 2026

Copy link
Copy Markdown
Member

Direct delegate to a static method on a value type boxes the ref-type first arg as the declaring struct → garbage / AccessViolationException

open System
[<Struct>]
type V =
    static member Pick (s: string, n: int) = s.Length + n

let make () = Func<int,int>(fun n -> V.Pick("abc", n))
// preview:  make().Invoke 10  ->  93584954   (expected 13)

let bad () = Func<int>(fun () -> Int32.Parse "41")
// preview:  bad().Invoke()    ->  System.AccessViolationException

Feature off / main: both correct. The emitted IL boxes the string as the struct:

ldstr   "abc"
box     P/V          // string reinterpreted as a boxed V
ldftn   int32 P/V::Pick(string, int32)
newobj  Func`2<int32,int32>::.ctor(object, native int)

Root cause: the closed-delegate emission boxes whenever targetMspec.DeclaringType.Boxity.IsAsValue (IlxGen.fs ~7703–7707). That is correct for a value-type instance receiver, but here the leading arg is the static method's first parameter — already a reference (guaranteed by staticLeadingArgIsRefType), so it must not be boxed.

Fix: only box when the receiver is a genuine value-type instance receiver (takesInstanceArg), not for the static closed-first-arg form. This path is currently untested — a struct-static positive test would have caught it.

Comment thread src/Compiler/CodeGen/IlxGen.fs
Comment thread src/Compiler/CodeGen/IlxGen.fs
@T-Gro

T-Gro commented Jul 24, 2026

Copy link
Copy Markdown
Member

🤖🕵️

Fixed in 8e5f373 — verified: make().Invoke 10 = 13 and bad().Invoke() = 41 (no AccessViolationException), and the erroneous box V is gone from the emitted IL. The added struct-static positive test covers it. Thanks!

@github-actions github-actions Bot added the AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files label Jul 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Tooling Safety Check — Affects-Compiler-Output
Affects-Compiler-Output: implements direct delegates codegen

Generated by PR Tooling Safety Check · opus46 12.1M ·

@github-project-automation github-project-automation Bot moved this from New to In Progress in F# Compiler and Tooling Jul 29, 2026
@T-Gro

T-Gro commented Aug 4, 2026

Copy link
Copy Markdown
Member

🤖🕵️
@kerams Re the null-receiver breaking change: it only happens for immutable receivers. Receivers bound to mutable cells (e.g. starts null, then reassigned) keep the closure — emitted IL is unchanged there, so the impact is avoided. Am I understanding this right?

@kerams

kerams commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Yes, mutable reads are considered effects, and an effectful receiver is disqualified from participating in direct delegates.

@T-Gro
T-Gro merged commit d89529c into dotnet:main Aug 4, 2026
49 of 50 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in F# Compiler and Tooling Aug 4, 2026
@kerams
kerams deleted the del branch August 4, 2026 10:50
T-Gro pushed a commit that referenced this pull request Aug 4, 2026
Brings the direct delegates feature into the darc dependency-update branch:
- New DirectDelegate optimization (src/Compiler/Optimize/DelegateForwarding.fs)
  wired into Optimizer.fs and IlxGen.fs codegen
- LanguageFeature flag + FSComp diagnostic (with localized xlf updates)
- EmittedIL DirectDelegates component tests and baselines
- Central Package Management migration (Directory.Packages.props,
  eng/Packages.props and per-project PackageReference/version updates)

Conflicts resolved by the previous session.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
T-Gro pushed a commit that referenced this pull request Aug 4, 2026
…#19993) into darc-main-bd030f95

Resolve eng/Versions.props conflict by adopting origin/main's Central Package Management structure (VS package versions live in eng/Packages.props). Bump the four VS pins Roslyn 5.11.0 requires higher than main's 5.10.0 baseline: Microsoft.VisualStudio.Threading 18.7.23, Microsoft.VisualStudio.RpcContracts 18.9.918, Microsoft.ServiceHub.Framework 4.10.147, StreamJsonRpc 2.26.10, and raise the shared editor pin VisualStudioEditorPackagesVersion to 18.9.179 (Roslyn 5.11.0 pulls the VS editor packages transitively at 18.9.179 -> NU1109 otherwise). Take origin/main's CPM form of Framework.Proxy.csproj (drop redundant Version-bearing PackageReferences superseded by central pins).

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

Labels

⚠️ Affects-Compiler-Output Tooling check: PR touches IL emission or codegen AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Don't emit unnecessary closures

4 participants