Skip to content

Commit 1e244bd

Browse files
authored
Nullness :: Improvement :: Overrides of generic methods with nullable type instantiations (#18337)
* issue analysis * No nullness warning if override passes in explicit nullable type argument * notes
1 parent 0dbf50c commit 1e244bd

File tree

5 files changed

+61
-4
lines changed

5 files changed

+61
-4
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
@@ -11,6 +11,7 @@
1111
* Cancellable: set token in more places ([PR #18283](https://github.com/dotnet/fsharp/pull/18283))
1212
* Cancellable: fix leaking cancellation token ([PR #18295](https://github.com/dotnet/fsharp/pull/18295))
1313
* Fix NRE when accessing nullable fields of types within their equals/hash/compare methods ([PR #18296](https://github.com/dotnet/fsharp/pull/18296))
14+
* Fix nullness warning for overrides of generic code with nullable type instance ([Issue #17988](https://github.com/dotnet/fsharp/issues/17988), [PR #18337](https://github.com/dotnet/fsharp/pull/18337))
1415

1516
### Added
1617
* Added missing type constraints in FCS. ([PR #18241](https://github.com/dotnet/fsharp/pull/18241))

src/Compiler/Checking/Expressions/CheckExpressions.fs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,7 +1890,11 @@ let FreshenAbstractSlot g amap m synTyparDecls absMethInfo =
18901890

18911891
// Work out the required type of the member
18921892
let argTysFromAbsSlot = argTys |> List.mapSquared (instType typarInstFromAbsSlot)
1893-
let retTyFromAbsSlot = retTy |> GetFSharpViewOfReturnType g |> instType typarInstFromAbsSlot
1893+
1894+
let retTyFromAbsSlot =
1895+
retTy
1896+
|> GetFSharpViewOfReturnType g
1897+
|> instType typarInstFromAbsSlot
18941898
typarsFromAbsSlotAreRigid, typarsFromAbsSlot, argTysFromAbsSlot, retTyFromAbsSlot
18951899

18961900
let CheckRecdExprDuplicateFields (elems: Ident list) =
@@ -11774,16 +11778,26 @@ and ApplyAbstractSlotInference (cenv: cenv) (envinner: TcEnv) (_: Val option) (a
1177411778
match uniqueAbstractMethSigs with
1177511779
| uniqueAbstractMeth :: _ ->
1177611780

11781+
// Overrides can narrow the retTy from nullable to not-null.
11782+
// By changing nullness to be variable we do not get in the way of eliminating nullness (=good).
11783+
// We only keep a WithNull nullness if it was part of an explicit type instantiation
11784+
let canChangeNullableRetTy =
11785+
match g.checkNullness, renaming with
11786+
| false, _ -> false
11787+
| true, [] -> true
11788+
| true, _ -> not(uniqueAbstractMeth.HasGenericRetTy())
11789+
1177711790
let uniqueAbstractMeth = uniqueAbstractMeth.Instantiate(cenv.amap, m, renaming)
1177811791

1177911792
let typarsFromAbsSlotAreRigid, typarsFromAbsSlot, argTysFromAbsSlot, retTyFromAbsSlot =
1178011793
FreshenAbstractSlot g cenv.amap m synTyparDecls uniqueAbstractMeth
1178111794

1178211795
let declaredTypars = (if typarsFromAbsSlotAreRigid then typarsFromAbsSlot else declaredTypars)
1178311796

11784-
// Overrides can narrow the retTy from nullable to not-null.
11785-
// By changing nullness to be variable we do not get in the way of eliminating nullness (=good).
11786-
let retTyFromAbsSlot = retTyFromAbsSlot |> changeWithNullReqTyToVariable g
11797+
let retTyFromAbsSlot =
11798+
if canChangeNullableRetTy then
11799+
retTyFromAbsSlot |> changeWithNullReqTyToVariable g
11800+
else retTyFromAbsSlot
1178711801

1178811802
let absSlotTy = mkMethodTy g argTysFromAbsSlot retTyFromAbsSlot
1178911803

src/Compiler/Checking/infos.fs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,6 +1430,20 @@ type MethInfo =
14301430
let (ParamAttribs(isParamArrayArg, isInArg, isOutArg, optArgInfo, callerInfo, reflArgInfo)) = info
14311431
ParamData(isParamArrayArg, isInArg, isOutArg, optArgInfo, callerInfo, nmOpt, reflArgInfo, pty)))
14321432

1433+
member x.HasGenericRetTy() =
1434+
match x with
1435+
| ILMeth(_g, ilminfo, _) -> ilminfo.RawMetadata.Return.Type.IsTypeVar
1436+
| FSMeth(g, _, vref, _) ->
1437+
let _, _, _, retTy, _ = GetTypeOfMemberInMemberForm g vref
1438+
match retTy with
1439+
| Some retTy -> isTyparTy g retTy
1440+
| None -> false
1441+
| MethInfoWithModifiedReturnType(_,retTy) -> false
1442+
| DefaultStructCtor _ -> false
1443+
#if !NO_TYPEPROVIDERS
1444+
| ProvidedMeth(amap, mi, _, m) -> false
1445+
#endif
1446+
14331447
/// Get the ParamData objects for the parameters of a MethInfo
14341448
member x.HasParamArrayArg(amap, m, minst) =
14351449
x.GetParamDatas(amap, m, minst) |> List.existsSquared (fun (ParamData(isParamArrayArg, _, _, _, _, _, _, _)) -> isParamArrayArg)

src/Compiler/Checking/infos.fsi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,8 @@ type MethInfo =
538538
/// Get the signature of an abstract method slot.
539539
member GetSlotSig: amap: ImportMap * m: range -> SlotSig
540540

541+
member HasGenericRetTy: unit -> bool
542+
541543
/// Get the ParamData objects for the parameters of a MethInfo
542544
member HasParamArrayArg: amap: ImportMap * m: range * minst: TType list -> bool
543545

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,6 +1382,32 @@ dict["ok"] <- 42
13821382
|> shouldSucceed
13831383

13841384

1385+
1386+
[<InlineData("null")>]
1387+
[<InlineData("if false then null else []")>]
1388+
[<InlineData("if false then [] else null")>]
1389+
[<InlineData("(if false then [] else null) : (_ list | null)")>]
1390+
[<InlineData("[] : (_ list | null)")>]
1391+
[<Theory>]
1392+
let ``Nullness in inheritance chain`` (returnExp:string) =
1393+
1394+
FSharp $"""module MyLibrary
1395+
1396+
[<AbstractClass>]
1397+
type Generator<'T>() =
1398+
abstract Values: unit -> 'T
1399+
1400+
[<Sealed>]
1401+
type ListGenerator<'T>() =
1402+
inherit Generator<List<'T> | null>()
1403+
1404+
override _.Values() = {returnExp}
1405+
"""
1406+
|> asLibrary
1407+
|> typeCheckWithStrictNullness
1408+
|> shouldSucceed
1409+
1410+
13851411
[<Fact>]
13861412
let ``Notnull constraint and inline annotated value`` () =
13871413
FSharp """module MyLibrary

0 commit comments

Comments
 (0)