Skip to content

Commit 3195041

Browse files
authored
Bugfix :: Support for 'use' on a nullable IDisposable (#18262)
* Support for 'use' on a nullable IDisposable * release notes
1 parent 5b910af commit 3195041

File tree

7 files changed

+22
-5
lines changed

7 files changed

+22
-5
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
@@ -7,6 +7,7 @@
77

88
### Added
99
* Added missing type constraints in FCS. ([PR #18241](https://github.com/dotnet/fsharp/pull/18241))
10+
* The 'use' keyword can be used on IDisposable|null without nullness warnings ([PR #18262](https://github.com/dotnet/fsharp/pull/18262))
1011

1112
### Changed
1213

src/Compiler/Checking/Expressions/CheckExpressions.fs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3097,7 +3097,7 @@ let BuildDisposableCleanup (cenv: cenv) env m (v: Val) =
30973097
else
30983098
mkUnit g m
30993099
else
3100-
let disposeObjVar, disposeObjExpr = mkCompGenLocal m "objectToDispose" g.system_IDisposable_ty
3100+
let disposeObjVar, disposeObjExpr = mkCompGenLocal m "objectToDispose" g.system_IDisposableNull_ty
31013101
let disposeExpr, _ = BuildPossiblyConditionalMethodCall cenv env PossiblyMutates m false disposeMethod NormalValUse [] [disposeObjExpr] [] None
31023102
let inputExpr = mkCoerceExpr(exprForVal v.Range v, g.obj_ty_ambivalent, m, v.Type)
31033103
mkIsInstConditional g m g.system_IDisposable_ty inputExpr disposeObjVar disposeExpr (mkUnit g m)
@@ -6811,7 +6811,7 @@ and TcCtorCall isNaked cenv env tpenv (overallTy: OverallTy) objTy mObjTyOpt ite
68116811
match item, args with
68126812
| Item.CtorGroup(methodName, minfos), _ ->
68136813
let meths = List.map (fun minfo -> minfo, None) minfos
6814-
if isNaked && TypeFeasiblySubsumesType 0 g cenv.amap mWholeCall g.system_IDisposable_ty NoCoerce objTy then
6814+
if isNaked && TypeFeasiblySubsumesType 0 g cenv.amap mWholeCall g.system_IDisposableNull_ty NoCoerce objTy then
68156815
warning(Error(FSComp.SR.tcIDisposableTypeShouldUseNew(), mWholeCall))
68166816

68176817
// Check the type is not abstract
@@ -11603,7 +11603,7 @@ and TcLetBinding (cenv: cenv) isUse env containerInfo declKind tpenv (synBinds,
1160311603
let isDiscarded = match checkedPat2 with TPat_wild _ -> true | _ -> false
1160411604
let allValsDefinedByPattern = if isDiscarded then [patternInputTmp] else allValsDefinedByPattern
1160511605
(allValsDefinedByPattern, (bodyExpr, bodyExprTy)) ||> List.foldBack (fun v (bodyExpr, bodyExprTy) ->
11606-
AddCxTypeMustSubsumeType ContextInfo.NoContext denv cenv.css v.Range NoTrace g.system_IDisposable_ty v.Type
11606+
AddCxTypeMustSubsumeType ContextInfo.NoContext denv cenv.css v.Range NoTrace g.system_IDisposableNull_ty v.Type
1160711607
let cleanupE = BuildDisposableCleanup cenv env m v
1160811608
mkTryFinally g (bodyExpr, cleanupE, m, bodyExprTy, DebugPointAtTry.No, DebugPointAtFinally.No), bodyExprTy)
1160911609
else

src/Compiler/Checking/Expressions/CheckExpressionsOps.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ let mkSeqEmpty (cenv: TcFileState) env m genTy =
297297

298298
let mkSeqUsing (cenv: TcFileState) (env: TcEnv) m resourceTy genTy resourceExpr lam =
299299
let g = cenv.g
300-
AddCxTypeMustSubsumeType ContextInfo.NoContext env.DisplayEnv cenv.css m NoTrace g.system_IDisposable_ty resourceTy
300+
AddCxTypeMustSubsumeType ContextInfo.NoContext env.DisplayEnv cenv.css m NoTrace g.system_IDisposableNull_ty resourceTy
301301
let genResultTy = NewInferenceType g
302302
UnifyTypes cenv env m genTy (mkSeqTy cenv.g genResultTy)
303303
mkCallSeqUsing cenv.g m resourceTy genResultTy resourceExpr lam

src/Compiler/Optimize/LowerComputedCollections.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ let BuildDisposableCleanup tcVal (g: TcGlobals) infoReader m (v: Val) =
3535

3636
disposeExpr
3737
else
38-
let disposeObjVar, disposeObjExpr = mkCompGenLocal m "objectToDispose" g.system_IDisposable_ty
38+
let disposeObjVar, disposeObjExpr = mkCompGenLocal m "objectToDispose" g.system_IDisposableNull_ty
3939
let disposeExpr, _ = BuildMethodCall tcVal g infoReader.amap PossiblyMutates m false disposeMethod NormalValUse [] [disposeObjExpr] [] None
4040
let inputExpr = mkCoerceExpr(exprForVal v.Range v, g.obj_ty_ambivalent, m, v.Type)
4141
mkIsInstConditional g m g.system_IDisposable_ty inputExpr disposeObjVar disposeExpr (mkUnit g m)

src/Compiler/TypedTree/TcGlobals.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,6 +1369,7 @@ type TcGlobals(
13691369
member val system_Array_ty = mkSysNonGenericTy sys "Array"
13701370
member val system_Object_ty = mkSysNonGenericTy sys "Object"
13711371
member val system_IDisposable_ty = mkSysNonGenericTy sys "IDisposable"
1372+
member val system_IDisposableNull_ty = mkNonGenericTyWithNullness (findSysTyconRef sys "IDisposable") v_knownWithNull
13721373
member val system_RuntimeHelpers_ty = mkSysNonGenericTy sysCompilerServices "RuntimeHelpers"
13731374
member val system_Value_ty = mkSysNonGenericTy sys "ValueType"
13741375
member val system_Delegate_ty = mkSysNonGenericTy sys "Delegate"

src/Compiler/TypedTree/TcGlobals.fsi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,8 @@ type internal TcGlobals =
11991199

12001200
member system_IDisposable_ty: FSharp.Compiler.TypedTree.TType
12011201

1202+
member system_IDisposableNull_ty: FSharp.Compiler.TypedTree.TType
1203+
12021204
member system_IFormattable_tcref: FSharp.Compiler.TypedTree.EntityRef
12031205

12041206
member system_IFormattable_ty: FSharp.Compiler.TypedTree.TType

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,19 @@ let safeHolder : IDisposable =
173173
|> typeCheckWithStrictNullness
174174
|> shouldSucceed
175175

176+
[<Fact>]
177+
let ``Can _use_ a nullable IDisposable`` () =
178+
FSharp """module TestLib
179+
open System
180+
let workWithResource (getD:int -> (IDisposable|null)) =
181+
use _ = getD 15
182+
15
183+
184+
"""
185+
|> asLibrary
186+
|> typeCheckWithStrictNullness
187+
|> shouldSucceed
188+
176189
[<Fact>]
177190
let ``Does not duplicate warnings`` () =
178191
FSharp """

0 commit comments

Comments
 (0)