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

Skip to content

Commit 6b2a50e

Browse files
T-GroCopilot
andauthored
Fix ValueOption optional args via ?param caller syntax (#19711) (#19742)
When the SupportValueOptionsAsOptionalParameters language feature is enabled, do not force option<_> on caller-side ?name = expr arguments during overload inference. Leave the type as a fresh inference variable and let AdjustCalledArgTypeForOptionals (CalleeSide) reconcile against option<_> or voption<_>. Pre-LangVersion-10 behaviour is preserved. Co-authored-by: Copilot <[email protected]>
1 parent 93ba0ea commit 6b2a50e

3 files changed

Lines changed: 91 additions & 5 deletions

File tree

docs/release-notes/.FSharp.Compiler.Service/11.0.100.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
* Fix parallel compilation of scripts ([PR #19649](https://github.com/dotnet/fsharp/pull/19649))
6060
* Fix parser recovery, name resolution, and code completion for unfinished enum patterns ([PR #19708](https://github.com/dotnet/fsharp/pull/19708))
6161
* Parser: fix unexpected diagnostics in debug builds, improve error messages ([PR #19730](https://github.com/dotnet/fsharp/pull/19730))
62+
* Fix `[<Struct>] ?param` optional parameters could not be passed using the explicit `?param = expr` caller-side syntax with a `ValueOption` value. ([Issue #19711](https://github.com/dotnet/fsharp/issues/19711), [PR #19742](https://github.com/dotnet/fsharp/pull/19742))
6263
* Fix signature conformance: overloaded member with unit parameter `M(())` now matches sig `member M: unit -> unit`. ([Issue #19596](https://github.com/dotnet/fsharp/issues/19596), [PR #19615](https://github.com/dotnet/fsharp/pull/19615))
6364
* Parser: recover on unfinished if and binary expressions
6465
([PR #19724](https://github.com/dotnet/fsharp/pull/19724))

src/Compiler/Checking/Expressions/CheckExpressions.fs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10170,13 +10170,21 @@ and TcMethodApplication_SplitSynArguments
1017010170

1017110171
| _ ->
1017210172
let unnamedCurriedCallerArgs = unnamedCurriedCallerArgs |> List.mapSquared MakeUnnamedCallerArgInfo
10173+
let supportsValueOptionalArgs =
10174+
g.langVersion.SupportsFeature LanguageFeature.SupportValueOptionsAsOptionalParameters
1017310175
let namedCurriedCallerArgs = namedCurriedCallerArgs |> List.mapSquared (fun (isOpt, nm, x) ->
1017410176
let ty = GetNewInferenceTypeForMethodArg cenv x
1017510177
// #435263: compiler crash with .net optional parameters and F# optional syntax
10176-
// named optional arguments should always have option type
10177-
// STRUCT OPTIONS: if we allow struct options as optional arguments then we should relax this and rely
10178-
// on later inference to work out if this is a struct option or ref option
10179-
let ty = if isOpt then mkOptionTy denv.g ty else ty
10178+
// For LangVersion < 10 named optional arguments are constrained to option type here
10179+
// so that an early, friendly error is reported. With LangVersion >= 10 the parameter
10180+
// may be a struct-option (voption) — see issue dotnet/fsharp#19711 — so we leave the
10181+
// type as a fresh inference variable and let later unification (CalleeSide branch in
10182+
// AdjustCalledArgTypeForOptionals) pick option<_> or voption<_>.
10183+
let ty =
10184+
if isOpt && not supportsValueOptionalArgs then
10185+
mkOptionTy denv.g ty
10186+
else
10187+
ty
1018010188
nm, isOpt, x, ty, x.Range)
1018110189

1018210190
(Some (unnamedCurriedCallerArgs, namedCurriedCallerArgs), None, exprTy)

tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/MemberDefinitions/OptionalArguments/OptionalArguments.fs

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -634,4 +634,81 @@ let main _args =
634634
|> ILVerifierModule.verifyPEFileWithSystemDlls
635635
|> run
636636
|> verifyOutputContains [|"main;18;hello;42;"|]
637-
637+
638+
// Regression test for https://github.com/dotnet/fsharp/issues/19711
639+
// `?name = expr` (explicit caller-side question-mark syntax) must accept a
640+
// ValueOption when the declared parameter is `[<Struct>] ?name`.
641+
[<Fact>]
642+
let ``ValueOption optional parameters can be passed using ?param syntax on methods`` () =
643+
FSharp """
644+
module Program
645+
type OptionalArguments() =
646+
member _.NoProblem (?number : int32) =
647+
match number with
648+
| Some v -> printfn "RSome %d" v
649+
| None -> printfn "RNone"
650+
member _.Problem ([<Struct>] ?number : int32) =
651+
match number with
652+
| ValueSome v -> printfn "VSome %d" v
653+
| ValueNone -> printfn "VNone"
654+
655+
[<EntryPoint>]
656+
let main _ =
657+
let o = OptionalArguments()
658+
o.NoProblem 123
659+
o.NoProblem (number = 123)
660+
o.NoProblem (?number = None)
661+
o.NoProblem (?number = Some 123)
662+
o.NoProblem (?number = Unchecked.defaultof<_>)
663+
o.Problem 123
664+
o.Problem (number = 123)
665+
o.Problem (?number = ValueNone)
666+
o.Problem (?number = ValueSome 123)
667+
o.Problem (?number = Unchecked.defaultof<_>)
668+
0
669+
"""
670+
|> asExe
671+
|> withOptions ["--nowarn:988"]
672+
|> compileAndRun
673+
|> shouldSucceed
674+
|> withOutputContainsAllInOrder [
675+
"RSome 123"; "RSome 123"; "RNone"; "RSome 123"; "RNone"
676+
"VSome 123"; "VSome 123"; "VNone"; "VSome 123"; "VNone"
677+
]
678+
679+
[<Fact>]
680+
let ``ValueOption optional parameters can be passed using ?param syntax on constructors`` () =
681+
FSharp """
682+
module Program
683+
type X<'T>([<Struct>] ?x : 'T) =
684+
member _.M() =
685+
match x with
686+
| ValueSome v -> printfn "VSome %A" v
687+
| ValueNone -> printfn "VNone"
688+
689+
[<EntryPoint>]
690+
let main _ =
691+
X<int>(?x = ValueSome 1).M()
692+
X<int>(?x = ValueNone).M()
693+
X<int>(?x = Unchecked.defaultof<_>).M()
694+
0
695+
"""
696+
|> asExe
697+
|> withOptions ["--nowarn:988"]
698+
|> compileAndRun
699+
|> shouldSucceed
700+
|> withOutputContainsAllInOrder ["VSome 1"; "VNone"; "VNone"]
701+
702+
[<Fact>]
703+
let ``ValueOption optional parameters via ?param syntax fail with langversion 9`` () =
704+
FSharp """
705+
module Program
706+
type X() =
707+
static member M ([<Struct>] ?x : int) = ()
708+
709+
X.M(?x = ValueSome 1)
710+
"""
711+
|> withLangVersion90
712+
|> typecheck
713+
|> shouldFail
714+
|> withDiagnosticMessageMatches "voption"

0 commit comments

Comments
 (0)