diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md index 5c444bd4fd3..d8207f7bd58 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md @@ -6,6 +6,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 diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 9987f02759c..f33c7239063 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -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) @@ -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 @@ -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 diff --git a/src/Compiler/Checking/Expressions/CheckExpressionsOps.fs b/src/Compiler/Checking/Expressions/CheckExpressionsOps.fs index 17572c86e4f..3552b1345f7 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressionsOps.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressionsOps.fs @@ -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 diff --git a/src/Compiler/Optimize/LowerComputedCollections.fs b/src/Compiler/Optimize/LowerComputedCollections.fs index 1f224ea499c..4e74722e1bf 100644 --- a/src/Compiler/Optimize/LowerComputedCollections.fs +++ b/src/Compiler/Optimize/LowerComputedCollections.fs @@ -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) diff --git a/src/Compiler/TypedTree/TcGlobals.fs b/src/Compiler/TypedTree/TcGlobals.fs index 4f4a45c735e..d8b95566717 100644 --- a/src/Compiler/TypedTree/TcGlobals.fs +++ b/src/Compiler/TypedTree/TcGlobals.fs @@ -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" diff --git a/src/Compiler/TypedTree/TcGlobals.fsi b/src/Compiler/TypedTree/TcGlobals.fsi index 73d26a64b62..772bc6b96d7 100644 --- a/src/Compiler/TypedTree/TcGlobals.fsi +++ b/src/Compiler/TypedTree/TcGlobals.fsi @@ -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 diff --git a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs index 819bd475152..7eec0ba8054 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs @@ -173,6 +173,19 @@ let safeHolder : IDisposable = |> typeCheckWithStrictNullness |> shouldSucceed +[] +let ``Can _use_ a nullable IDisposable`` () = + FSharp """module TestLib +open System +let workWithResource (getD:int -> (IDisposable|null)) = + use _ = getD 15 + 15 + + """ + |> asLibrary + |> typeCheckWithStrictNullness + |> shouldSucceed + [] let ``Does not duplicate warnings`` () = FSharp """