diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md index 7f0be7cb37f..822a85e72ac 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md @@ -24,6 +24,7 @@ * Reenable β-reduction and subsequent reoptimization of immediately-invoked F#-defined generic delegates. ([PR #18401](https://github.com/dotnet/fsharp/pull/18401)) * Fixed [#18433](https://github.com/dotnet/fsharp/issues/18433), a rare case of an internal error in xml comment processing. ([PR #18436](https://github.com/dotnet/fsharp/pull/18436)) * Fix missing `null` highlighting in tooltips ([PR #18457](https://github.com/dotnet/fsharp/pull/18457)) +* Make `[]` combination work([PR #18444](https://github.com/dotnet/fsharp/pull/18444/)) ### Added * Added missing type constraints in FCS. ([PR #18241](https://github.com/dotnet/fsharp/pull/18241)) diff --git a/src/Compiler/Checking/MethodCalls.fs b/src/Compiler/Checking/MethodCalls.fs index 41d731f007e..19b91dcf0b1 100644 --- a/src/Compiler/Checking/MethodCalls.fs +++ b/src/Compiler/Checking/MethodCalls.fs @@ -348,6 +348,12 @@ let inline mkOptionalNone (g: TcGlobals) ty calledArgTy mMethExpr = else mkNone g calledArgTy mMethExpr +let inline mkOptionalSome (g: TcGlobals) outerOptTy innerNonOptionalType expr mMethExpr = + if g.langVersion.SupportsFeature LanguageFeature.SupportValueOptionsAsOptionalParameters && isValueOptionTy g outerOptTy then + mkValueSome g innerNonOptionalType expr mMethExpr + else + mkSome g innerNonOptionalType expr mMethExpr + /// Adjust the called argument type to take into account whether the caller's argument is CSharpMethod(?arg=Some(3)) or CSharpMethod(arg=1) let AdjustCalledArgTypeForOptionals (infoReader: InfoReader) ad enforceNullableOptionalsKnownTypes (calledArg: CalledArg) calledArgTy (callerArg: CallerArg<_>) = @@ -1502,14 +1508,14 @@ let GetDefaultExpressionForCalleeSideOptionalArg g (calledArg: CalledArg) eCalle match calledArg.CallerInfo, eCallerMemberName with | CallerLineNumber, _ when typeEquiv g calledNonOptTy g.int_ty -> let lineExpr = Expr.Const(Const.Int32 mMethExpr.StartLine, mMethExpr, calledNonOptTy) - mkSome g calledNonOptTy lineExpr mMethExpr + mkOptionalSome g calledArgTy calledNonOptTy lineExpr mMethExpr | CallerFilePath, _ when typeEquiv g calledNonOptTy g.string_ty -> let fileName = mMethExpr.FileName |> FileSystem.GetFullPathShim |> PathMap.apply g.pathMap let filePathExpr = Expr.Const (Const.String(fileName), mMethExpr, calledNonOptTy) - mkSome g calledNonOptTy filePathExpr mMethExpr + mkOptionalSome g calledArgTy calledNonOptTy filePathExpr mMethExpr | CallerMemberName, Some(callerName) when typeEquiv g calledNonOptTy g.string_ty -> let memberNameExpr = Expr.Const (Const.String callerName, mMethExpr, calledNonOptTy) - mkSome g calledNonOptTy memberNameExpr mMethExpr + mkOptionalSome g calledArgTy calledNonOptTy memberNameExpr mMethExpr | _ -> mkOptionalNone g calledArgTy calledNonOptTy mMethExpr diff --git a/src/Compiler/Checking/PostInferenceChecks.fs b/src/Compiler/Checking/PostInferenceChecks.fs index d22f50cdc2f..751112e3918 100644 --- a/src/Compiler/Checking/PostInferenceChecks.fs +++ b/src/Compiler/Checking/PostInferenceChecks.fs @@ -2398,13 +2398,25 @@ let CheckEntityDefn cenv env (tycon: Entity) = errorR(Error(FSComp.SR.chkCurriedMethodsCantHaveOutParams(), m)) if numCurriedArgSets = 1 then - let errorIfNotStringTy m ty callerInfo = + + let inline tryDestOptionalTy g ty = + if isOptionTy g ty then + destOptionTy g ty |> ValueSome + elif g.langVersion.SupportsFeature LanguageFeature.SupportValueOptionsAsOptionalParameters && isValueOptionTy g ty then + destValueOptionTy g ty |> ValueSome + else + ValueNone + + let errorIfNotStringTy m ty callerInfo = if not (typeEquiv g g.string_ty ty) then errorR(Error(FSComp.SR.tcCallerInfoWrongType(callerInfo |> string, "string", NicePrint.minimalStringOfType cenv.denv ty), m)) + + let errorIfNotOptional tyToCompare desiredTyName m ty callerInfo = - let errorIfNotStringOptionTy m ty callerInfo = - if not ((isOptionTy g ty) && (typeEquiv g g.string_ty (destOptionTy g ty))) then - errorR(Error(FSComp.SR.tcCallerInfoWrongType(callerInfo |> string, "string", NicePrint.minimalStringOfType cenv.denv (destOptionTy g ty)), m)) + match tryDestOptionalTy g ty with + | ValueSome t when typeEquiv g tyToCompare t -> () + | ValueSome innerTy -> errorR(Error(FSComp.SR.tcCallerInfoWrongType(callerInfo |> string, desiredTyName, NicePrint.minimalStringOfType cenv.denv innerTy), m)) + | ValueNone -> errorR(Error(FSComp.SR.tcCallerInfoWrongType(callerInfo |> string, desiredTyName, NicePrint.minimalStringOfType cenv.denv ty), m)) minfo.GetParamDatas(cenv.amap, m, minfo.FormalMethodInst) |> List.iterSquared (fun (ParamData(_, isInArg, _, optArgInfo, callerInfo, nameOpt, _, ty)) -> @@ -2421,11 +2433,9 @@ let CheckEntityDefn cenv env (tycon: Entity) = | CallerSide _, CallerLineNumber -> if not (typeEquiv g g.int32_ty ty) then errorR(Error(FSComp.SR.tcCallerInfoWrongType(callerInfo |> string, "int", NicePrint.minimalStringOfType cenv.denv ty), m)) - | CalleeSide, CallerLineNumber -> - if not ((isOptionTy g ty) && (typeEquiv g g.int32_ty (destOptionTy g ty))) then - errorR(Error(FSComp.SR.tcCallerInfoWrongType(callerInfo |> string, "int", NicePrint.minimalStringOfType cenv.denv (destOptionTy g ty)), m)) + | CalleeSide, CallerLineNumber -> errorIfNotOptional g.int32_ty "int" m ty callerInfo | CallerSide _, (CallerFilePath | CallerMemberName) -> errorIfNotStringTy m ty callerInfo - | CalleeSide, (CallerFilePath | CallerMemberName) -> errorIfNotStringOptionTy m ty callerInfo + | CalleeSide, (CallerFilePath | CallerMemberName) -> errorIfNotOptional g.string_ty "string" m ty callerInfo ) for pinfo in immediateProps do diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/MemberDefinitions/OptionalArguments/OptionalArguments.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/MemberDefinitions/OptionalArguments/OptionalArguments.fs index c0604630d56..4d0843014e0 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/MemberDefinitions/OptionalArguments/OptionalArguments.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/MemberDefinitions/OptionalArguments/OptionalArguments.fs @@ -609,4 +609,41 @@ but here has type ''T option' but here has type ''a voption' " - ] \ No newline at end of file + ] + + [] + let ``Struct optional args can have caller member name`` () = + + let source = """module TestLib +open System.Runtime.CompilerServices + +let printItOut x = + printf "%s" $"{x};" + +type Ab() = + + static member aa ([]?b: string) = + printItOut b + + static member bb ([]?i: int) = + printItOut i + +[] +let main _args = + Ab.aa() + Ab.bb() + Ab.aa("hello") + Ab.bb(42) + 0 +""" + + source + |> FSharp + |> withLangVersionPreview + |> withNoWarn 25 + |> asExe + |> compile + |> ILVerifierModule.verifyPEFileWithSystemDlls + |> run + |> verifyOutputContains [|"main;18;hello;42;"|] + \ No newline at end of file diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index e21079818d7..2b3ccface37 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -1640,7 +1640,8 @@ Actual: | Some (ExecutionOutput {Outcome = Failure ex }) -> failwithf $"Eval or Execution has failed (expected to succeed): %A{ex}\n{diagnostics}" | _ -> - failwithf $"Operation failed (expected to succeed).\n{diagnostics}" + + failwithf $"Operation failed (expected to succeed).\n{diagnostics} \n OUTPUTs: %A{r.Output}" let shouldFail (result: CompilationResult) : CompilationResult = match result with