From 6ba4d2c672151596086a71e132ffee73b8ae91af Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 16 Oct 2025 09:16:14 +0000
Subject: [PATCH 1/3] Initial plan
From fd8508f58f0eff6eb6c8f224341467e83357ce07 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 16 Oct 2025 09:50:56 +0000
Subject: [PATCH 2/3] Add FSharpPlus regression tests for F# 9 issues
Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
---
.../FSharp.Compiler.ComponentTests.fsproj | 1 +
.../Language/FSharpPlusRegressionTests.fs | 130 ++++++++++++++++++
2 files changed, 131 insertions(+)
create mode 100644 tests/FSharp.Compiler.ComponentTests/Language/FSharpPlusRegressionTests.fs
diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj
index 29847415be2..c3641d31f65 100644
--- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj
+++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj
@@ -234,6 +234,7 @@
+
diff --git a/tests/FSharp.Compiler.ComponentTests/Language/FSharpPlusRegressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/FSharpPlusRegressionTests.fs
new file mode 100644
index 00000000000..5aa448516d4
--- /dev/null
+++ b/tests/FSharp.Compiler.ComponentTests/Language/FSharpPlusRegressionTests.fs
@@ -0,0 +1,130 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
+
+namespace Language
+
+open System
+open Xunit
+open FSharp.Test.ScriptHelpers
+open FSharp.Compiler.Diagnostics
+
+module FSharpPlusRegressionTests =
+
+ ///
+ /// Regression test for FSharpPlus issue #613 - monad.plus usage scenario.
+ /// This test reproduces a consumer-side failure where using monad.plus in F# 9
+ /// causes compilation issues. The code should compile successfully.
+ /// Issue: https://github.com/fsprojects/FSharpPlus/issues/613
+ ///
+ []
+ []
+ []
+ []
+ let ``monad.plus usage should compile successfully`` (langVersion: string, [] additionalArgs: string[]) =
+ let allArgs = Array.concat [[| "--langversion:" + langVersion |]; additionalArgs]
+ use script = new FSharpScript(additionalArgs = allArgs, quiet = true)
+
+ let code = """
+// Simulated monad.plus pattern from FSharpPlus
+// This pattern uses statically resolved type parameters (SRTP) for ad-hoc polymorphism
+type MonadPlusClass =
+ static member inline MPlus (x: option<'a>, y: option<'a>) =
+ match x with
+ | Some _ -> x
+ | None -> y
+
+ static member inline MPlus (x: list<'a>, y: list<'a>) = x @ y
+
+// Generic mplus function using SRTP to dispatch to appropriate implementation
+let inline mplus (x: ^M) (y: ^M) : ^M =
+ ((^MonadPlusClass or ^M) : (static member MPlus : ^M * ^M -> ^M) (x, y))
+
+// Direct usage with concrete types
+let testOption() =
+ let result : int option = mplus (Some 1) (Some 2)
+ printfn "Option result = %A" result
+
+let testList() =
+ let result : int list = mplus [1; 2] [3; 4]
+ printfn "List result = %A" result
+
+testOption()
+testList()
+"""
+
+ let evalResult, diagnostics = script.Eval(code)
+
+ // The code should compile successfully
+ match evalResult with
+ | Ok _ ->
+ // Filter out informational diagnostics
+ let errors = diagnostics |> Array.filter (fun d ->
+ d.Severity = FSharpDiagnosticSeverity.Error)
+ Assert.Empty(errors)
+ | Error ex ->
+ Assert.True(false, sprintf "Evaluation failed with exception: %s\nDiagnostics: %A" ex.Message diagnostics)
+
+ ///
+ /// Regression test for FSharpPlus issue #613 - custom ResultTBuilder scenario.
+ /// This test reproduces a consumer-side failure where defining a custom ResultTBuilder
+ /// in F# 9 causes compilation issues. The code should compile successfully.
+ /// Issue: https://github.com/fsprojects/FSharpPlus/issues/613
+ ///
+ []
+ []
+ []
+ []
+ let ``custom ResultTBuilder should compile successfully`` (langVersion: string, [] additionalArgs: string[]) =
+ let allArgs = Array.concat [[| "--langversion:" + langVersion |]; additionalArgs]
+ use script = new FSharpScript(additionalArgs = allArgs, quiet = true)
+
+ let code = """
+// Custom ResultTBuilder pattern from FSharpPlus
+type ResultTBuilder() =
+ member inline _.Return(x: 'T) : Result<'T, 'Error> = Ok x
+
+ member inline _.ReturnFrom(m: Result<'T, 'Error>) : Result<'T, 'Error> = m
+
+ member inline _.Bind(m: Result<'T, 'Error>, f: 'T -> Result<'U, 'Error>) : Result<'U, 'Error> =
+ match m with
+ | Ok x -> f x
+ | Error e -> Error e
+
+ member inline _.Zero() : Result = Ok ()
+
+ member inline _.Combine(m1: Result, m2: Result<'T, 'Error>) : Result<'T, 'Error> =
+ match m1 with
+ | Ok () -> m2
+ | Error e -> Error e
+
+ member inline _.Delay(f: unit -> Result<'T, 'Error>) : unit -> Result<'T, 'Error> = f
+
+ member inline _.Run(f: unit -> Result<'T, 'Error>) : Result<'T, 'Error> = f()
+
+let resultT = ResultTBuilder()
+
+// Usage example
+let compute x y =
+ resultT {
+ let! a = Ok x
+ let! b = Ok y
+ return a + b
+ }
+
+// Apply the function to avoid value restriction
+let testResult : Result = compute 5 10
+
+// Verify result
+printfn "testResult = %A" testResult
+"""
+
+ let evalResult, diagnostics = script.Eval(code)
+
+ // The code should compile successfully
+ match evalResult with
+ | Ok _ ->
+ // Filter out informational diagnostics
+ let errors = diagnostics |> Array.filter (fun d ->
+ d.Severity = FSharpDiagnosticSeverity.Error)
+ Assert.Empty(errors)
+ | Error ex ->
+ Assert.True(false, sprintf "Evaluation failed with exception: %s\nDiagnostics: %A" ex.Message diagnostics)
From 0fafa17c432a945bed5ba619335430e999e4908a Mon Sep 17 00:00:00 2001
From: Tomas Grosup
Date: Fri, 5 Dec 2025 10:38:11 +0100
Subject: [PATCH 3/3] address fsharpplus consumer regression
---
src/Compiler/Checking/CheckDeclarations.fs | 2 +-
.../Conformance/Inference/SRTP.fs | 33 ++++++++
.../Conformance/Inference/SRTP_NuGet.fs | 25 ++++++
.../FSharp.Compiler.ComponentTests.fsproj | 2 +
.../FSharpPlusRegressionTests.fs | 78 +++++++++++++++++++
5 files changed, 139 insertions(+), 1 deletion(-)
create mode 100644 tests/FSharp.Compiler.ComponentTests/Conformance/Inference/SRTP.fs
create mode 100644 tests/FSharp.Compiler.ComponentTests/Conformance/Inference/SRTP_NuGet.fs
create mode 100644 tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharpPlusRegressionTests.fs
diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs
index e230cd48280..28c7cb46ce2 100644
--- a/src/Compiler/Checking/CheckDeclarations.fs
+++ b/src/Compiler/Checking/CheckDeclarations.fs
@@ -5650,7 +5650,7 @@ let rec IterTyconsOfModuleOrNamespaceType f (mty: ModuleOrNamespaceType) =
// Defaults get applied in priority order. Defaults listed last get priority 0 (lowest), 2nd last priority 1 etc.
let ApplyDefaults (cenv: cenv) g denvAtEnd m moduleContents extraAttribs =
try
- let unsolved = FindUnsolved.UnsolvedTyparsOfModuleDef g cenv.amap denvAtEnd moduleContents extraAttribs
+ let unsolved = FindUnsolved.UnsolvedTyparsOfModuleDef g cenv.amap denvAtEnd moduleContents extraAttribs |> List.rev
CanonicalizePartialInferenceProblem cenv.css denvAtEnd m unsolved
diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Inference/SRTP.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Inference/SRTP.fs
new file mode 100644
index 00000000000..8276c80a9ef
--- /dev/null
+++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Inference/SRTP.fs
@@ -0,0 +1,33 @@
+namespace FSharp.Compiler.ComponentTests.Conformance.Inference
+
+open FSharp.Test
+open Xunit
+
+module SRTP =
+
+ []
+ let ``SRTP resolution with curryN and Tuple`` () =
+ let source = """
+module SRTP_Repro
+
+open System
+
+type Curry =
+ static member inline Invoke f =
+ let inline call_2 (a: ^a, b: ^b) = ((^a or ^b) : (static member Curry: _*_ -> _) b, a)
+ call_2 (Unchecked.defaultof, Unchecked.defaultof<'t>) (f: 't -> 'r) : 'args
+
+ static member Curry (_: Tuple<'t1> , _: Curry) = fun f t1 -> f (Tuple<_> t1)
+ static member Curry (_: Tuple<'t1, 't2> , _: Curry) = fun f t1 t2 -> f (Tuple<_,_>(t1, t2))
+
+let inline curryN (f: (^``T1 * ^T2 * ... * ^Tn``) -> 'Result) : 'T1 -> '``T2 -> ... -> 'Tn -> 'Result`` = fun t -> Curry.Invoke f t
+
+let f1 (x: Tuple<_>) = [x.Item1]
+let f2 (x: Tuple<_,_>) = [x.Item1; x.Item2]
+
+let test () =
+ let _x1 = curryN f1 100
+ let _x2 = curryN f2 10 20
+ ()
+"""
+ CompilerAssert.TypeCheckWithErrors(source)
diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Inference/SRTP_NuGet.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Inference/SRTP_NuGet.fs
new file mode 100644
index 00000000000..7a34e5a8945
--- /dev/null
+++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Inference/SRTP_NuGet.fs
@@ -0,0 +1,25 @@
+namespace FSharp.Compiler.ComponentTests.Conformance.Inference
+
+open FSharp.Test
+open Xunit
+
+module SRTP_NuGet =
+
+ []
+ let ``SRTP resolution with curryN and Tuple from FSharpPlus NuGet`` () =
+ CompilerAssert.RunScriptWithOptions
+ [| "--langversion:preview"; "--source"; "https://api.nuget.org/v3/index.json" |]
+ """
+#r "nuget: FSharpPlus, 1.6.1"
+open FSharpPlus
+open System
+
+let f1 (x: Tuple<_>) = [x.Item1]
+
+let test () =
+ let _x1 = curryN f1 100
+ ()
+
+test()
+"""
+ []
diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj
index c22d5f95004..adb463ec232 100644
--- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj
+++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj
@@ -128,6 +128,8 @@
+
+
diff --git a/tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharpPlusRegressionTests.fs b/tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharpPlusRegressionTests.fs
new file mode 100644
index 00000000000..dc52eb6a019
--- /dev/null
+++ b/tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharpPlusRegressionTests.fs
@@ -0,0 +1,78 @@
+namespace FSharp.Compiler.Scripting.UnitTests
+
+open System
+open FSharp.Compiler.Interactive.Shell
+open FSharp.Test.ScriptHelpers
+open Xunit
+
+type FSharpPlusRegressionTests() =
+
+ []
+ member _.``FSharpPlus Regression Test 1``() =
+ use script = new FSharpScript()
+ let code = """
+#r "nuget: FSharpPlus, 1.8.0"
+
+open FSharpPlus
+
+let y: seq<_> = monad.plus {
+ for x in seq [1..3] do
+ for y in seq [10; 20] do
+ return (x, y)
+}
+"""
+ let result, errors = script.Eval(code)
+ if errors.Length > 0 then
+ let msg = errors |> Array.map (fun e -> e.Message) |> String.concat "\n"
+ Assert.Fail($"Script failed with errors:\n{msg}")
+
+ match result with
+ | Ok(_) -> ()
+ | Error(ex) -> Assert.Fail($"Script failed with exception: {ex}")
+
+ []
+ member _.``FSharpPlus Regression Test 2``() =
+ use script = new FSharpScript()
+ let code = """
+#r "nuget: FSharpPlus, 1.8.0"
+
+open FSharpPlus
+open FSharpPlus.Data
+
+type AsyncResult<'T, 'E> = ResultT>>
+
+type ResultTBuilder<'``monad>``>() =
+ inherit Builder>``>>()
+
+ member inline _.For (x: ResultT<'``Monad>``>, f: 'T -> ResultT<'``Monad>``>) = x >>= f : ResultT<'``Monad>``>
+
+ []
+ member inline _.Lift (x: ResultT<'``Monad>``>, m: '``Monad<'U>``, f: 'T -> 'U -> 'V) =
+ x >>= fun a ->
+ lift m |> ResultT.bind (fun b ->
+ result (f a b) : ResultT<'``Monad>``>)
+
+let resultT<'``Monad>``> = new ResultTBuilder<'``Monad>``>()
+
+let sampleWorkflow2 =
+ monad {
+ let! x = Some 1
+ let! y = Some 2
+ return x + y
+ }
+
+let test2 () =
+ resultT {
+ let! x = ResultT.hoist (Ok 1)
+ lift y in sampleWorkflow2
+ return x + y
+ }
+"""
+ let result, errors = script.Eval(code)
+ if errors.Length > 0 then
+ let msg = errors |> Array.map (fun e -> e.Message) |> String.concat "\n"
+ Assert.Fail($"Script failed with errors:\n{msg}")
+
+ match result with
+ | Ok(_) -> ()
+ | Error(ex) -> Assert.Fail($"Script failed with exception: {ex}")