Skip to content

Commit 10b812b

Browse files
authored
Bugfix :: Flexible types should subsume nullable version of equivalent CoarcesTo constraints (#18266)
1 parent 1f9b0ce commit 10b812b

File tree

4 files changed

+73
-2
lines changed

4 files changed

+73
-2
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
### Fixed
22
* Fix Realsig+ generates nested closures with incorrect Generic ([Issue #17797](https://github.com/dotnet/fsharp/issues/17797), [PR #17877](https://github.com/dotnet/fsharp/pull/17877))
33
* Fix optimizer internal error for records with static fields ([Issue #18165](https://github.com/dotnet/fsharp/issues/18165), [PR #18280](https://github.com/dotnet/fsharp/pull/18280))
4+
* Fix nullness warning with flexible types ([Issue #18056](https://github.com/dotnet/fsharp/issues/18056), [PR #18266](https://github.com/dotnet/fsharp/pull/18266))
45
* Fix internal error when missing measure attribute in an unsolved measure typar. ([Issue #7491](https://github.com/dotnet/fsharp/issues/7491), [PR #18234](https://github.com/dotnet/fsharp/pull/18234)==
56
* Set `Cancellable.token` from async computation ([Issue #18235](https://github.com/dotnet/fsharp/issues/18235), [PR #18238](https://github.com/dotnet/fsharp/pull/18238))
67
* Fix missing nullness warning when static upcast dropped nullness ([Issue #18232](https://github.com/dotnet/fsharp/issues/18232), [PR #18261](https://github.com/dotnet/fsharp/pull/18261))

src/Compiler/Checking/ConstraintSolver.fs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1609,7 +1609,10 @@ and SolveTyparSubtypeOfType (csenv: ConstraintSolverEnv) ndeep m2 trace tp ty1 =
16091609
elif isSealedTy g ty1 then
16101610
SolveTypeEqualsTypeKeepAbbrevs csenv ndeep m2 trace (mkTyparTy tp) ty1
16111611
else
1612-
AddConstraint csenv ndeep m2 trace tp (TyparConstraint.CoercesTo(ty1, csenv.m))
1612+
if SubtypeConstraintImplied g tp.Constraints ty1 then
1613+
CompleteD
1614+
else
1615+
AddConstraint csenv ndeep m2 trace tp (TyparConstraint.CoercesTo(ty1, csenv.m))
16131616

16141617
and DepthCheck ndeep m =
16151618
if ndeep > 300 then
@@ -2490,6 +2493,19 @@ and CheckConstraintImplication (csenv: ConstraintSolverEnv) tpc1 tpc2 =
24902493
and CheckConstraintsImplication csenv existingConstraints newConstraint =
24912494
existingConstraints |> List.exists (fun tpc2 -> CheckConstraintImplication csenv tpc2 newConstraint)
24922495

2496+
and SubtypeConstraintImplied g existingConstraints newCoarceToTy =
2497+
if g.checkNullness then
2498+
let canBeNull t = (nullnessOfTy g t).Evaluate() = NullnessInfo.WithNull
2499+
let newTyIsWithoutNull = canBeNull newCoarceToTy |> not
2500+
let typeCoversNewConstraint existingTy =
2501+
typeEquiv g existingTy newCoarceToTy
2502+
&& not (newTyIsWithoutNull && canBeNull existingTy) // :> T? cannot imply :>T, since non-nullable is a stricter constraint.
2503+
2504+
existingConstraints
2505+
|> List.exists (function | TyparConstraint.CoercesTo(ty2,_) when typeCoversNewConstraint ty2 -> true | _ -> false)
2506+
else
2507+
false
2508+
24932509
// Ensure constraint conforms with existing constraints
24942510
// NOTE: QUADRATIC
24952511
and EnforceConstraintSetConsistency csenv ndeep m2 trace retry allCxs i cxs =

tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1003,7 +1003,45 @@ looseFunc(maybeTuple2) |> ignore
10031003
Error 3261, Line 29, Col 12, Line 29, Col 19, "Nullness warning: The type 'MyDu | null' supports 'null' but a non-null type is expected."
10041004
Error 3261, Line 30, Col 12, Line 30, Col 21, "Nullness warning: The type 'MyRecord | null' supports 'null' but a non-null type is expected."
10051005
Error 43, Line 40, Col 36, Line 40, Col 40, "The type 'Maybe<int * int>' does not have 'null' as a proper value"]
1006-
1006+
1007+
1008+
[<Fact>]
1009+
let ``Nullness support for flexible types`` () =
1010+
FSharp """module MyLibrary
1011+
open System
1012+
let dispose (x: IDisposable | null) : unit =
1013+
match x with
1014+
| null -> ()
1015+
| d -> d.Dispose()
1016+
1017+
let useThing (thing: #IDisposable) =
1018+
try
1019+
printfn "%O" thing
1020+
finally
1021+
dispose thing // Warning used to be here, should not warn! """
1022+
|> asLibrary
1023+
|> typeCheckWithStrictNullness
1024+
|> shouldSucceed
1025+
1026+
[<Fact>]
1027+
let ``Nullness support for flexible types - opposite`` () =
1028+
FSharp """module MyLibrary
1029+
open System
1030+
let dispose (x: IDisposable) : unit =
1031+
x.Dispose()
1032+
1033+
let useThing (thing: #IDisposable | null) =
1034+
try
1035+
printfn "%O" thing
1036+
finally
1037+
dispose thing // Warning should be here, because 'thing' can be null!
1038+
"""
1039+
|> asLibrary
1040+
|> typeCheckWithStrictNullness
1041+
|> shouldFail
1042+
|> withDiagnostics [Error 3261, Line 10, Col 17, Line 10, Col 22, "Nullness warning: The types 'IDisposable' and ''a | null' do not have compatible nullability."]
1043+
1044+
10071045
[<Fact>]
10081046
let ``Static member on Record with null arg`` () =
10091047
FSharp """module MyLibrary

tests/FSharp.Compiler.ComponentTests/Signatures/SeqTests.fs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
open Xunit
44
open Signatures.TestHelpers
5+
open FSharp.Test
6+
open FSharp.Test.Compiler
57

68
[<Fact>]
79
let ``int seq`` () =
@@ -14,3 +16,17 @@ let ``tuple seq`` () =
1416
assertSingleSignatureBinding
1517
"let s = seq { yield (1, 'b', 2.) }"
1618
"val s: (int * char * float) seq"
19+
20+
[<Fact>]
21+
let ``seq transpose`` () =
22+
let encodeFs =
23+
FsSource """module Program
24+
let transpose (source: seq<#seq<'T>>) =
25+
source |> Seq.collect Seq.indexed |> Seq.groupBy fst |> Seq.map (snd >> (Seq.map snd))"""
26+
27+
Fsi """module Program
28+
val transpose: source: seq<'Collection> -> seq<seq<'T>> when 'Collection :> seq<'T>"""
29+
|> withAdditionalSourceFile encodeFs
30+
|> withLangVersionPreview
31+
|> compile
32+
|> shouldSucceed

0 commit comments

Comments
 (0)