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/Expressions/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9934,7 +9934,7 @@ and TcMethodApplication_SplitSynArguments
// named optional arguments should always have option type
// STRUCT OPTIONS: if we allow struct options as optional arguments then we should relax this and rely
// on later inference to work out if this is a struct option or ref option
let ty = if isOpt then mkOptionTy denv.g ty else ty
let ty = if isOpt && not (denv.g.langVersion.SupportsFeature LanguageFeature.SupportValueOptionsAsOptionalParameters) then mkOptionTy denv.g ty else ty
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn't look like it's the right fix.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its super confused, but it knew what to fix when just brainstorming about it :((

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're absolutely right. I've reverted the incorrect fix. The issue requires changes to the type checking/unification logic that handles optional parameters, not just disabling the wrapping at line 9937. The tests are in place to validate once the proper fix is implemented.

nm, isOpt, x, ty, x.Range)

(Some (unnamedCurriedCallerArgs, namedCurriedCallerArgs), None, exprTy)
Expand Down
2 changes: 1 addition & 1 deletion src/Compiler/Checking/MethodCalls.fs
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ let AdjustCalledArgTypeForOptionals (infoReader: InfoReader) ad enforceNullableO

// FSharpMethod(?x = arg), optional F#-style argument
| CalleeSide ->
// In this case, the called argument will already have option type
// In this case, the called argument will already have option/voption type
calledArgTy, TypeDirectedConversionUsed.No, None

| NotOptional ->
Expand Down
83 changes: 83 additions & 0 deletions tests/fsharp/Compiler/Language/OptionalInteropTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,86 @@ Test.OverloadedMethodTakingNullableOptionals(x = 6) |> ignore
let fs = Compilation.Create(fsSrc, CompileOutput.Exe, options = [| $"--langversion:{langVersion}" |], cmplRefs = [cs])
CompilerAssert.Compile fs

// Tests for issue #19074: Support voption with optional parameter syntax (?x=)
[<Fact>]
let ``F# method with voption optional parameter should accept ValueNone with question mark syntax`` () =
let fsSrc =
"""
open System.Runtime.InteropServices

type MyClass() =
member _.Foo([<Optional>] x: int voption) =
match x with
| ValueNone -> "none"
| ValueSome v -> string v

let c = MyClass()
let r1 = c.Foo(?x=ValueNone)
printfn "%s" r1
"""

let fs = Compilation.Create(fsSrc, CompileOutput.Exe, options = [| "--langversion:preview" |])
CompilerAssert.Compile fs

[<Fact>]
let ``F# method with voption optional parameter should accept ValueSome with question mark syntax`` () =
let fsSrc =
"""
open System.Runtime.InteropServices

type MyClass() =
member _.Foo([<Optional>] x: int voption) =
match x with
| ValueNone -> "none"
| ValueSome v -> string v

let c = MyClass()
let r2 = c.Foo(?x=ValueSome 42)
printfn "%s" r2
"""

let fs = Compilation.Create(fsSrc, CompileOutput.Exe, options = [| "--langversion:preview" |])
CompilerAssert.Compile fs

[<Fact>]
let ``F# method with voption optional parameter should work without question mark syntax`` () =
let fsSrc =
"""
open System.Runtime.InteropServices

type MyClass() =
member _.Foo([<Optional>] x: int voption) =
match x with
| ValueNone -> "none"
| ValueSome v -> string v

let c = MyClass()
let r1 = c.Foo(x=ValueNone)
let r2 = c.Foo(x=ValueSome 42)
printfn "%s %s" r1 r2
"""

let fs = Compilation.Create(fsSrc, CompileOutput.Exe, options = [| "--langversion:preview" |])
CompilerAssert.Compile fs

[<Fact>]
let ``F# method with option optional parameter should still work with question mark syntax`` () =
let fsSrc =
"""
open System.Runtime.InteropServices

type MyClass() =
member _.Foo([<Optional>] x: int option) =
match x with
| None -> "none"
| Some v -> string v

let c = MyClass()
let r1 = c.Foo(?x=None)
let r2 = c.Foo(?x=Some 42)
printfn "%s %s" r1 r2
"""

let fs = Compilation.Create(fsSrc, CompileOutput.Exe, options = [| "--langversion:preview" |])
CompilerAssert.Compile fs

Loading