diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md index 16495b7b1ca..10f1b7b2ec0 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -12,6 +12,7 @@ * Improve let-rec codegen: reorder bindings to allocate lambda closures before non-lambda values that reference them. ([PR #19339](https://github.com/dotnet/fsharp/pull/19339)) * Fix `YieldFromFinal`/`ReturnFromFinal` being incorrectly called in non-tail positions (`for`, `use`, `use!`, `try/with` handler). ([Issue #19402](https://github.com/dotnet/fsharp/issues/19402), [PR #19403](https://github.com/dotnet/fsharp/pull/19403)) * Fixed how the source ranges of warn directives are reported (as trivia) in the parser output (by not reporting leading spaces). ([Issue #19405](https://github.com/dotnet/fsharp/issues/19405), [PR #19408]((https://github.com/dotnet/fsharp/pull/19408))) +* Fix UoM value type `ToString()` returning garbage values when `--checknulls+` is enabled, caused by double address-taking in codegen. ([Issue #19435](https://github.com/dotnet/fsharp/issues/19435), [PR #19440](https://github.com/dotnet/fsharp/pull/19440)) ### Added diff --git a/src/Compiler/Checking/MethodCalls.fs b/src/Compiler/Checking/MethodCalls.fs index 285dfa4fa8c..170767afb62 100644 --- a/src/Compiler/Checking/MethodCalls.fs +++ b/src/Compiler/Checking/MethodCalls.fs @@ -1231,11 +1231,17 @@ let rec BuildMethodCall tcVal g amap isMutable m isProp minfo valUseFlags minst let vExpr, vExprTy = tcVal vref valUseFlags (minfo.DeclaringTypeInst @ minst) m BuildFSharpMethodApp g m vref vExpr vExprTy allArgs - | MethInfoWithModifiedReturnType(mi,retTy) -> - let expr, exprTy = BuildMethodCall tcVal g amap isMutable m isProp mi valUseFlags minst objArgs args staticTyOpt - let expr = mkCoerceExpr(expr, retTy, m, exprTy) + | MethInfoWithModifiedReturnType(ILMeth(_, ilMethInfo, _), retTy) -> + // Build the inner call directly, without re-invoking TakeObjAddrForMethodCall. + let expr, exprTy = + BuildILMethInfoCall g amap m isProp ilMethInfo valUseFlags minst direct allArgs + + let expr = mkCoerceExpr (expr, retTy, m, exprTy) expr, retTy + | MethInfoWithModifiedReturnType _ -> + failwith "MethInfoWithModifiedReturnType: unexpected inner method kind" + // Build a 'call' to a struct default constructor | DefaultStructCtor (g, ty) -> if g.langFeatureNullness && g.checkNullness then diff --git a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs index 31dffcfb169..c36c51b3a15 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs @@ -734,6 +734,29 @@ let processAlias (x:mykgalias) = |> typeCheckWithStrictNullness |> shouldSucceed +// https://github.com/dotnet/fsharp/issues/19435 +[] +[", "42")>] +[", "42")>] +[", "42")>] +let ``ToString on value type with UoM does not produce garbage at runtime`` (valueExpr: string, expected: string) = + FSharp $""" +module MyProgram + +[] +type mykg + +[] +let main _ = + let x = {valueExpr} + System.Console.Write(x.ToString()) + 0 + """ + |> withCheckNulls + |> compileExeAndRun + |> shouldSucceed + |> withStdOutContains expected + [] let ``Printing a nullable string should pass`` () = FSharp """module MyLibrary