diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md index fe3cde0444a..fd5a94681ec 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md @@ -5,6 +5,7 @@ * Fix extension methods support for non-reference system assemblies ([PR #17799](https://github.com/dotnet/fsharp/pull/17799)) * Ensure `frameworkTcImportsCache` mutations are thread-safe. ([PR #17795](https://github.com/dotnet/fsharp/pull/17795)) * Fix concurrency issue in `ILPreTypeDefImpl` ([PR #17812](https://github.com/dotnet/fsharp/pull/17812)) +* Fix nullness inference for member val and other OO scenarios ([PR #17845](https://github.com/dotnet/fsharp/pull/17845)) ### Added diff --git a/src/Compiler/TypedTree/TypedTree.fs b/src/Compiler/TypedTree/TypedTree.fs index b948e91fb65..b5e620c6bd6 100644 --- a/src/Compiler/TypedTree/TypedTree.fs +++ b/src/Compiler/TypedTree/TypedTree.fs @@ -4358,6 +4358,12 @@ type NullnessVar() = member nv.IsSolved = solution.IsSome + member nv.IsFullySolved = + match solution with + | None -> false + | Some (Nullness.Known _) -> true + | Some (Nullness.Variable v) -> v.IsFullySolved + member nv.Set(nullness) = assert (not nv.IsSolved) solution <- Some nullness diff --git a/src/Compiler/TypedTree/TypedTree.fsi b/src/Compiler/TypedTree/TypedTree.fsi index d357895728d..5f96fba2266 100644 --- a/src/Compiler/TypedTree/TypedTree.fsi +++ b/src/Compiler/TypedTree/TypedTree.fsi @@ -3100,6 +3100,7 @@ type NullnessVar = member Evaluate: unit -> NullnessInfo member TryEvaluate: unit -> NullnessInfo voption member IsSolved: bool + member IsFullySolved: bool member Set: Nullness -> unit member Unset: unit -> unit member Solution: Nullness diff --git a/src/Compiler/TypedTree/TypedTreeBasics.fs b/src/Compiler/TypedTree/TypedTreeBasics.fs index c8268ffcf8a..0ad62482b6a 100644 --- a/src/Compiler/TypedTree/TypedTreeBasics.fs +++ b/src/Compiler/TypedTree/TypedTreeBasics.fs @@ -284,7 +284,7 @@ let tryAddNullnessToTy nullnessNew (ty:TType) = let addNullnessToTy (nullness: Nullness) (ty:TType) = match nullness with | Nullness.Known NullnessInfo.WithoutNull -> ty - | Nullness.Variable nv when nv.IsSolved && nv.Evaluate() = NullnessInfo.WithoutNull -> ty + | Nullness.Variable nv when nv.IsFullySolved && nv.TryEvaluate() = ValueSome NullnessInfo.WithoutNull -> ty | _ -> match ty with | TType_var (tp, nullnessOrig) -> TType_var (tp, combineNullness nullnessOrig nullness) diff --git a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs index 1edfbfd7961..0da1169b9a8 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs @@ -144,6 +144,21 @@ let nonStrictFunc(x:string | null) = strictFunc(x) |> shouldFail |> withDiagnostics [ Error 3261, Line 4, Col 49, Line 4, Col 50, "Nullness warning: The types 'string' and 'string | null' do not have equivalent nullability."] + +[] +let ``Can have nullable prop of same type T within a custom type T``() = + FSharp """ +module MyLib +type T () = + let mutable v : T | null = null + member val P : T | null = null with get, set + member this.M() = + v <- null + this.P <- null + """ + |> asLibrary + |> typeCheckWithStrictNullness + |> shouldSucceed [] []