Skip to content

Commit

Permalink
Bugfix :: Support for 'use' on a nullable IDisposable (#18262)
Browse files Browse the repository at this point in the history
* Support for 'use' on a nullable IDisposable

* release notes
  • Loading branch information
T-Gro authored Feb 3, 2025
1 parent 5b910af commit 3195041
Show file tree
Hide file tree
Showing 7 changed files with 22 additions and 5 deletions.
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/9.0.300.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

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

### Changed

Expand Down
6 changes: 3 additions & 3 deletions src/Compiler/Checking/Expressions/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3097,7 +3097,7 @@ let BuildDisposableCleanup (cenv: cenv) env m (v: Val) =
else
mkUnit g m
else
let disposeObjVar, disposeObjExpr = mkCompGenLocal m "objectToDispose" g.system_IDisposable_ty
let disposeObjVar, disposeObjExpr = mkCompGenLocal m "objectToDispose" g.system_IDisposableNull_ty
let disposeExpr, _ = BuildPossiblyConditionalMethodCall cenv env PossiblyMutates m false disposeMethod NormalValUse [] [disposeObjExpr] [] None
let inputExpr = mkCoerceExpr(exprForVal v.Range v, g.obj_ty_ambivalent, m, v.Type)
mkIsInstConditional g m g.system_IDisposable_ty inputExpr disposeObjVar disposeExpr (mkUnit g m)
Expand Down Expand Up @@ -6811,7 +6811,7 @@ and TcCtorCall isNaked cenv env tpenv (overallTy: OverallTy) objTy mObjTyOpt ite
match item, args with
| Item.CtorGroup(methodName, minfos), _ ->
let meths = List.map (fun minfo -> minfo, None) minfos
if isNaked && TypeFeasiblySubsumesType 0 g cenv.amap mWholeCall g.system_IDisposable_ty NoCoerce objTy then
if isNaked && TypeFeasiblySubsumesType 0 g cenv.amap mWholeCall g.system_IDisposableNull_ty NoCoerce objTy then
warning(Error(FSComp.SR.tcIDisposableTypeShouldUseNew(), mWholeCall))

// Check the type is not abstract
Expand Down Expand Up @@ -11603,7 +11603,7 @@ and TcLetBinding (cenv: cenv) isUse env containerInfo declKind tpenv (synBinds,
let isDiscarded = match checkedPat2 with TPat_wild _ -> true | _ -> false
let allValsDefinedByPattern = if isDiscarded then [patternInputTmp] else allValsDefinedByPattern
(allValsDefinedByPattern, (bodyExpr, bodyExprTy)) ||> List.foldBack (fun v (bodyExpr, bodyExprTy) ->
AddCxTypeMustSubsumeType ContextInfo.NoContext denv cenv.css v.Range NoTrace g.system_IDisposable_ty v.Type
AddCxTypeMustSubsumeType ContextInfo.NoContext denv cenv.css v.Range NoTrace g.system_IDisposableNull_ty v.Type
let cleanupE = BuildDisposableCleanup cenv env m v
mkTryFinally g (bodyExpr, cleanupE, m, bodyExprTy, DebugPointAtTry.No, DebugPointAtFinally.No), bodyExprTy)
else
Expand Down
2 changes: 1 addition & 1 deletion src/Compiler/Checking/Expressions/CheckExpressionsOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ let mkSeqEmpty (cenv: TcFileState) env m genTy =

let mkSeqUsing (cenv: TcFileState) (env: TcEnv) m resourceTy genTy resourceExpr lam =
let g = cenv.g
AddCxTypeMustSubsumeType ContextInfo.NoContext env.DisplayEnv cenv.css m NoTrace g.system_IDisposable_ty resourceTy
AddCxTypeMustSubsumeType ContextInfo.NoContext env.DisplayEnv cenv.css m NoTrace g.system_IDisposableNull_ty resourceTy
let genResultTy = NewInferenceType g
UnifyTypes cenv env m genTy (mkSeqTy cenv.g genResultTy)
mkCallSeqUsing cenv.g m resourceTy genResultTy resourceExpr lam
Expand Down
2 changes: 1 addition & 1 deletion src/Compiler/Optimize/LowerComputedCollections.fs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ let BuildDisposableCleanup tcVal (g: TcGlobals) infoReader m (v: Val) =

disposeExpr
else
let disposeObjVar, disposeObjExpr = mkCompGenLocal m "objectToDispose" g.system_IDisposable_ty
let disposeObjVar, disposeObjExpr = mkCompGenLocal m "objectToDispose" g.system_IDisposableNull_ty
let disposeExpr, _ = BuildMethodCall tcVal g infoReader.amap PossiblyMutates m false disposeMethod NormalValUse [] [disposeObjExpr] [] None
let inputExpr = mkCoerceExpr(exprForVal v.Range v, g.obj_ty_ambivalent, m, v.Type)
mkIsInstConditional g m g.system_IDisposable_ty inputExpr disposeObjVar disposeExpr (mkUnit g m)
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/TypedTree/TcGlobals.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1369,6 +1369,7 @@ type TcGlobals(
member val system_Array_ty = mkSysNonGenericTy sys "Array"
member val system_Object_ty = mkSysNonGenericTy sys "Object"
member val system_IDisposable_ty = mkSysNonGenericTy sys "IDisposable"
member val system_IDisposableNull_ty = mkNonGenericTyWithNullness (findSysTyconRef sys "IDisposable") v_knownWithNull
member val system_RuntimeHelpers_ty = mkSysNonGenericTy sysCompilerServices "RuntimeHelpers"
member val system_Value_ty = mkSysNonGenericTy sys "ValueType"
member val system_Delegate_ty = mkSysNonGenericTy sys "Delegate"
Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/TypedTree/TcGlobals.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -1199,6 +1199,8 @@ type internal TcGlobals =

member system_IDisposable_ty: FSharp.Compiler.TypedTree.TType

member system_IDisposableNull_ty: FSharp.Compiler.TypedTree.TType

member system_IFormattable_tcref: FSharp.Compiler.TypedTree.EntityRef

member system_IFormattable_ty: FSharp.Compiler.TypedTree.TType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,19 @@ let safeHolder : IDisposable =
|> typeCheckWithStrictNullness
|> shouldSucceed

[<Fact>]
let ``Can _use_ a nullable IDisposable`` () =
FSharp """module TestLib
open System
let workWithResource (getD:int -> (IDisposable|null)) =
use _ = getD 15
15
"""
|> asLibrary
|> typeCheckWithStrictNullness
|> shouldSucceed

[<Fact>]
let ``Does not duplicate warnings`` () =
FSharp """
Expand Down

0 comments on commit 3195041

Please sign in to comment.