Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Compiler/Checking/CheckDeclarations.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace FSharp.Compiler.ComponentTests.Conformance.Inference

open FSharp.Test
open Xunit

module SRTP =

[<Fact>]
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<Curry>, 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)
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace FSharp.Compiler.ComponentTests.Conformance.Inference

open FSharp.Test
open Xunit

module SRTP_NuGet =

[<FSharp.Test.FactSkipOnSignedBuild>]
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()
"""
[]
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@
<Compile Include="Conformance\Types\StructTypes\StructActivePatterns.fs" />
<Compile Include="Conformance\Types\TypeConstraints\CheckingSyntacticTypes\CheckingSyntacticTypes.fs" />
<Compile Include="Conformance\Types\TypeConstraints\LogicalPropertiesOfTypes\LogicalPropertiesOfTypes.fs" />
<Compile Include="Conformance\Inference\SRTP.fs" />
<Compile Include="Conformance\Inference\SRTP_NuGet.fs" />
<Compile Include="Conformance\Types\TypeConstraints\IWSAMsAndSRTPs\IWSAMsAndSRTPsTests.fs" />
<Compile Include="Conformance\Types\UnionTypes\UnionTypes.fs" />
<Compile Include="Conformance\Types\UnionTypes\UnionStructTypes.fs" />
Expand Down Expand Up @@ -234,6 +236,7 @@
<Compile Include="Language\IndexerSetterParamArray.fs" />
<Compile Include="Language\MultiDimensionalArrayTests.fs" />
<Compile Include="Language\RegressionTests.fs" />
<Compile Include="Language\FSharpPlusRegressionTests.fs" />
<Compile Include="Language\AttributeCheckingTests.fs" />
<Compile Include="Language\ObsoleteAttributeCheckingTests.fs" />
<Compile Include="Language\ExperimentalAttributeCheckingTests.fs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -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 =

/// <summary>
/// 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
/// </summary>
[<Theory>]
[<InlineData("8.0")>]
[<InlineData("preview")>]
[<InlineData("preview", "--checknulls+")>]
let ``monad.plus usage should compile successfully`` (langVersion: string, [<ParamArray>] 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)

/// <summary>
/// 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
/// </summary>
[<Theory>]
[<InlineData("8.0")>]
[<InlineData("preview")>]
[<InlineData("preview", "--checknulls+")>]
let ``custom ResultTBuilder should compile successfully`` (langVersion: string, [<ParamArray>] 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<unit, 'Error> = Ok ()

member inline _.Combine(m1: Result<unit, 'Error>, 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<int, string> = 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)
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
namespace FSharp.Compiler.Scripting.UnitTests

open System
open FSharp.Compiler.Interactive.Shell
open FSharp.Test.ScriptHelpers
open Xunit

type FSharpPlusRegressionTests() =

[<Fact>]
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}")

[<Fact>]
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<Async<Result<'T, 'E>>>

type ResultTBuilder<'``monad<Result<'t, 'e>>``>() =
inherit Builder<ResultT<'``monad<Result<'t, 'e>>``>>()

member inline _.For (x: ResultT<'``Monad<Result<'T, 'E>>``>, f: 'T -> ResultT<'``Monad<Result<'U, 'E>>``>) = x >>= f : ResultT<'``Monad<Result<'U, 'E>>``>

[<CustomOperation("lift", IsLikeZip=true)>]
member inline _.Lift (x: ResultT<'``Monad<Result<'T, 'E>>``>, m: '``Monad<'U>``, f: 'T -> 'U -> 'V) =
x >>= fun a ->
lift m |> ResultT.bind (fun b ->
result (f a b) : ResultT<'``Monad<Result<'V, 'E>>``>)

let resultT<'``Monad<Result<'T, 'E>>``> = new ResultTBuilder<'``Monad<Result<'T, 'E>>``>()

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}")
Loading