Skip to content

Make [<CallerMemberName; Struct>] combination work #18444

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/9.0.300.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `[<CallerMemberName; Struct>]` 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))
Expand Down
12 changes: 9 additions & 3 deletions src/Compiler/Checking/MethodCalls.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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<_>) =
Expand Down Expand Up @@ -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

Expand Down
26 changes: 18 additions & 8 deletions src/Compiler/Checking/PostInferenceChecks.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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)) ->
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -609,4 +609,41 @@ but here has type
''T option'
but here has type
''a voption' "
]
]

[<Fact>]
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 ([<CallerMemberName; Struct>]?b: string) =
printItOut b

static member bb ([<CallerLineNumber; Struct>]?i: int) =
printItOut i

[<EntryPoint>]
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;"|]

3 changes: 2 additions & 1 deletion tests/FSharp.Test.Utilities/Compiler.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down