diff --git a/.fantomasignore b/.fantomasignore index 9c12ffa8e2c..f5798fbdf00 100644 --- a/.fantomasignore +++ b/.fantomasignore @@ -41,6 +41,11 @@ src/Compiler/Checking/TypeHierarchy.fs src/Compiler/Checking/TypeRelations.fs # nullness-related problems +src/Compiler/Utilities/lib.fsi +src/Compiler/Utilities/Cancellable.fsi +src/FSharp.Core/tasks.fsi +src/FSharp.Core/tasks.fs +src/FSharp.Core/resumable.fs src/Compiler/DependencyManager/DependencyProvider.fs src/FSharp.Core/fslib-extra-pervasives.fs src/FSharp.Core/fslib-extra-pervasives.fsi 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 f41b4ca9289..c766a1804f0 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md @@ -19,6 +19,7 @@ * FSharpCheckFileResults.ProjectContext.ProjectOptions will not be available when using the experimental Transparent Compiler feature. ([PR #18205](https://github.com/dotnet/fsharp/pull/18205)) * Update `Obsolete` attribute checking to account for `DiagnosticId` and `UrlFormat` properties. ([PR #18224](https://github.com/dotnet/fsharp/pull/18224)) * Remove `Cancellable.UsingToken` from tests ([PR #18276](https://github.com/dotnet/fsharp/pull/18276)) +* Added nullability annotations to `.Using` builder method for `async`, `task` and compiler-internal builders ([PR #18292](https://github.com/dotnet/fsharp/pull/18292)) ### Breaking Changes * Struct unions with overlapping fields now generate mappings needed for reading via reflection ([Issue #18121](https://github.com/dotnet/fsharp/issues/17797), [PR #18274](https://github.com/dotnet/fsharp/pull/17877)) \ No newline at end of file diff --git a/docs/release-notes/.FSharp.Core/9.0.300.md b/docs/release-notes/.FSharp.Core/9.0.300.md index 87d3502539c..5e6903f3fa4 100644 --- a/docs/release-notes/.FSharp.Core/9.0.300.md +++ b/docs/release-notes/.FSharp.Core/9.0.300.md @@ -1,8 +1,9 @@ ### Fixed ### Added +* Added nullability annotations to `.Using` builder method for `async` and `task` builders ([PR #18292](https://github.com/dotnet/fsharp/pull/18292)) ### Changed ### Breaking Changes -* Struct unions with overlapping fields now generate mappings needed for reading via reflection ([Issue #18121](https://github.com/dotnet/fsharp/issues/17797), [PR #18274](https://github.com/dotnet/fsharp/pull/17877)). Previous versions of FSharp.Core returned incomplete mapping between fields and cases, these older fslib versions will now report an exception. +* Struct unions with overlapping fields now generate mappings needed for reading via reflection ([Issue #18121](https://github.com/dotnet/fsharp/issues/17797), [PR #18274](https://github.com/dotnet/fsharp/pull/18274)). Previous versions of FSharp.Core returned incomplete mapping between fields and cases, these older fslib versions will now report an exception. diff --git a/src/Compiler/Utilities/Activity.fs b/src/Compiler/Utilities/Activity.fs index 2403f00d3de..e2d7e78fcb6 100644 --- a/src/Compiler/Utilities/Activity.fs +++ b/src/Compiler/Utilities/Activity.fs @@ -89,18 +89,18 @@ module internal Activity = let private activitySource = new ActivitySource(ActivityNames.FscSourceName) - let start (name: string) (tags: (string * string) seq) : IDisposable = + let start (name: string) (tags: (string * string) seq) : IDisposable MaybeNull = let activity = activitySource.CreateActivity(name, ActivityKind.Internal) match activity with - | null -> !!activity //TODO change retTy to |null after PR #18262 is merged!! + | null -> activity | activity -> for key, value in tags do activity.AddTag(key, value) |> ignore activity.Start() - let startNoTags (name: string) : IDisposable = !! (activitySource.StartActivity name) //TODO change retTy to |null after PR #18262 is merged!! + let startNoTags (name: string) : IDisposable MaybeNull = activitySource.StartActivity name let addEvent name = match Activity.Current with @@ -122,7 +122,7 @@ module internal Activity = let private profiledSource = new ActivitySource(ActivityNames.ProfiledSourceName) - let startAndMeasureEnvironmentStats (name: string) : IDisposable = !!(profiledSource.StartActivity(name)) //TODO change retTy to |null after PR #18262 is merged!! + let startAndMeasureEnvironmentStats (name: string) : IDisposable MaybeNull = profiledSource.StartActivity(name) type private GCStats = int[] diff --git a/src/Compiler/Utilities/Activity.fsi b/src/Compiler/Utilities/Activity.fsi index ec6a9fbf6f8..6e7244f5e59 100644 --- a/src/Compiler/Utilities/Activity.fsi +++ b/src/Compiler/Utilities/Activity.fsi @@ -2,6 +2,7 @@ namespace FSharp.Compiler.Diagnostics open System +open Internal.Utilities.Library /// For activities following the dotnet distributed tracing concept /// https://learn.microsoft.com/dotnet/core/diagnostics/distributed-tracing-concepts?source=recommendations @@ -39,14 +40,14 @@ module internal Activity = module Events = val cacheHit: string - val startNoTags: name: string -> IDisposable + val startNoTags: name: string -> IDisposable MaybeNull - val start: name: string -> tags: (string * string) seq -> IDisposable + val start: name: string -> tags: (string * string) seq -> IDisposable MaybeNull val addEvent: name: string -> unit module Profiling = - val startAndMeasureEnvironmentStats: name: string -> IDisposable + val startAndMeasureEnvironmentStats: name: string -> IDisposable MaybeNull val addConsoleListener: unit -> IDisposable module CsvExport = diff --git a/src/Compiler/Utilities/Cancellable.fs b/src/Compiler/Utilities/Cancellable.fs index ad739b5039e..4d6c42cd6f0 100644 --- a/src/Compiler/Utilities/Cancellable.fs +++ b/src/Compiler/Utilities/Cancellable.fs @@ -162,7 +162,7 @@ type CancellableBuilder() = | Choice2Of2 err -> Cancellable.run ct (handler err) | ValueOrCancelled.Cancelled err1 -> ValueOrCancelled.Cancelled err1) - member inline _.Using(resource, [] comp) = + member inline _.Using(resource: _ MaybeNull, [] comp) = Cancellable(fun ct -> #if !FSHARPCORE_USE_PACKAGE __debugPoint "" diff --git a/src/Compiler/Utilities/Cancellable.fsi b/src/Compiler/Utilities/Cancellable.fsi index aba96859491..737a27a4f2a 100644 --- a/src/Compiler/Utilities/Cancellable.fsi +++ b/src/Compiler/Utilities/Cancellable.fsi @@ -71,8 +71,12 @@ type internal CancellableBuilder = comp: Cancellable<'T> * [] handler: (exn -> Cancellable<'T>) -> Cancellable<'T> member inline Using: - resource: 'Resource * [] comp: ('Resource -> Cancellable<'T>) -> Cancellable<'T> + resource: 'Resource MaybeNull * [] comp: ('Resource MaybeNull -> Cancellable<'T>) -> Cancellable<'T> when 'Resource :> IDisposable + and 'Resource:not struct +#if !(NO_CHECKNULLS || BUILDING_WITH_LKG) + and 'Resource:not null +#endif member inline Zero: unit -> Cancellable diff --git a/src/Compiler/Utilities/lib.fs b/src/Compiler/Utilities/lib.fs index 921b0a6dba3..609245f636e 100755 --- a/src/Compiler/Utilities/lib.fs +++ b/src/Compiler/Utilities/lib.fs @@ -403,7 +403,10 @@ type DisposablesTracker() = let items = Stack() /// Register some items to dispose - member _.Register i = items.Push i + member _.Register (i:#IDisposable MaybeNull) = + match box i with + | null -> () + | _ -> items.Push (!!i) interface IDisposable with diff --git a/src/Compiler/Utilities/lib.fsi b/src/Compiler/Utilities/lib.fsi index 08b834d17c6..ea0669bdf4a 100644 --- a/src/Compiler/Utilities/lib.fsi +++ b/src/Compiler/Utilities/lib.fsi @@ -263,7 +263,12 @@ type DisposablesTracker = new: unit -> DisposablesTracker /// Register some items to dispose - member Register: i: System.IDisposable -> unit + member Register: i:'a MaybeNull -> unit + when 'a:>System.IDisposable +#if !(NO_CHECKNULLS || BUILDING_WITH_LKG) + and 'a:not null +#endif + and 'a:not struct interface System.IDisposable diff --git a/src/FSharp.Core/async.fs b/src/FSharp.Core/async.fs index 959d47d0aab..2b117be0b16 100644 --- a/src/FSharp.Core/async.fs +++ b/src/FSharp.Core/async.fs @@ -753,7 +753,7 @@ module AsyncPrimitives = /// - Cancellation check after 'entering' the implied try/finally and before running the body (see CreateTryFinallyAsync) /// - Hijack check after 'entering' the implied try/finally and before running the body (see CreateTryFinallyAsync) /// - Run 'disposeFunction' with exception protection (see CreateTryFinallyAsync) - let CreateUsingAsync (resource: 'T :> IDisposable) (computation: 'T -> Async<'a>) : Async<'a> = + let CreateUsingAsync (resource: 'T :> IDisposable | null) (computation: 'T -> Async<'a>) : Async<'a> = let disposeFunction () = Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicFunctions.Dispose resource diff --git a/src/FSharp.Core/async.fsi b/src/FSharp.Core/async.fsi index e01ba0d8d75..d4227d292f5 100644 --- a/src/FSharp.Core/async.fsi +++ b/src/FSharp.Core/async.fsi @@ -1325,7 +1325,7 @@ namespace Microsoft.FSharp.Control /// An asynchronous computation that binds and eventually disposes resource. /// /// - member Using: resource:'T * binder:('T -> Async<'U>) -> Async<'U> when 'T :> System.IDisposable + member Using: resource:'T * binder:('T -> Async<'U>) -> Async<'U> when 'T :> System.IDisposable|null /// Creates an asynchronous computation that runs computation, and when /// computation generates a result T, runs binder res. diff --git a/src/FSharp.Core/prim-types.fs b/src/FSharp.Core/prim-types.fs index 1fc9e799ab4..4f4849eb849 100644 --- a/src/FSharp.Core/prim-types.fs +++ b/src/FSharp.Core/prim-types.fs @@ -739,8 +739,11 @@ namespace Microsoft.FSharp.Core let inline TypeTestFast<'T>(source: objnull) = //assert not(TypeInfo<'T>.TypeInfo = TypeNullnessSemantics_NullTrueValue) notnullPrim(isinstPrim<'T>(source)) - +#if !BUILDING_WITH_LKG && !NO_NULLCHECKING_LIB_SUPPORT + let Dispose<'T when 'T :> IDisposable >(resource:'T|null) = +#else let Dispose<'T when 'T :> IDisposable >(resource:'T) = +#endif match box resource with | null -> () | _ -> resource.Dispose() diff --git a/src/FSharp.Core/prim-types.fsi b/src/FSharp.Core/prim-types.fsi index 17f5a6418b0..c56fe5375e4 100644 --- a/src/FSharp.Core/prim-types.fsi +++ b/src/FSharp.Core/prim-types.fsi @@ -1796,7 +1796,11 @@ namespace Microsoft.FSharp.Core /// A compiler intrinsic for the efficient compilation of sequence expressions [] +#if !BUILDING_WITH_LKG && !NO_NULLCHECKING_LIB_SUPPORT + val Dispose<'T when 'T :> System.IDisposable> : resource: 'T|null -> unit +#else val Dispose<'T when 'T :> System.IDisposable> : resource: 'T -> unit +#endif /// A compiler intrinsic for checking initialization soundness of recursive bindings [] diff --git a/src/FSharp.Core/resumable.fs b/src/FSharp.Core/resumable.fs index 27e91a93256..1488ab220cf 100644 --- a/src/FSharp.Core/resumable.fs +++ b/src/FSharp.Core/resumable.fs @@ -399,7 +399,7 @@ module ResumableCode = ( resource: 'Resource, body: 'Resource -> ResumableCode<'Data, 'T> - ) : ResumableCode<'Data, 'T> when 'Resource :> IDisposable = + ) : ResumableCode<'Data, 'T> when 'Resource :> IDisposable|null = // A using statement is just a try/finally with the finally block disposing if non-null. TryFinally( ResumableCode<'Data, 'T>(fun sm -> (body resource).Invoke(&sm)), diff --git a/src/FSharp.Core/resumable.fsi b/src/FSharp.Core/resumable.fsi index 466301dc36b..2835b75a4dc 100644 --- a/src/FSharp.Core/resumable.fsi +++ b/src/FSharp.Core/resumable.fsi @@ -90,7 +90,7 @@ module ResumableCode = val inline TryWith: body: ResumableCode<'Data, 'T> * catch: (exn -> ResumableCode<'Data, 'T>) -> ResumableCode<'Data, 'T> /// Specifies resumable code which executes with 'use' semantics - val inline Using: resource: 'Resource * body: ('Resource -> ResumableCode<'Data, 'T>) -> ResumableCode<'Data, 'T> when 'Resource :> IDisposable + val inline Using: resource: 'Resource * body: ('Resource -> ResumableCode<'Data, 'T>) -> ResumableCode<'Data, 'T> when 'Resource :> IDisposable|null /// Specifies resumable code which executes a loop val inline While: [] condition: (unit -> bool) * body: ResumableCode<'Data, unit> -> ResumableCode<'Data, unit> diff --git a/src/FSharp.Core/tasks.fs b/src/FSharp.Core/tasks.fs index faaa920cb15..dfe7f4ba624 100644 --- a/src/FSharp.Core/tasks.fs +++ b/src/FSharp.Core/tasks.fs @@ -138,7 +138,7 @@ type TaskBuilderBase() = false) ) - member inline this.Using<'Resource, 'TOverall, 'T when 'Resource :> IAsyncDisposable> + member inline this.Using<'Resource, 'TOverall, 'T when 'Resource :> IAsyncDisposable|null> ( resource: 'Resource, body: 'Resource -> TaskCode<'TOverall, 'T> @@ -382,7 +382,7 @@ module LowPriority = this.Bind(task, this.Return) - member inline _.Using<'Resource, 'TOverall, 'T when 'Resource :> IDisposable> + member inline _.Using<'Resource, 'TOverall, 'T when 'Resource :> IDisposable|null> ( resource: 'Resource, body: 'Resource -> TaskCode<'TOverall, 'T> diff --git a/src/FSharp.Core/tasks.fsi b/src/FSharp.Core/tasks.fsi index 47713a60a3f..c626cb4bb7b 100644 --- a/src/FSharp.Core/tasks.fsi +++ b/src/FSharp.Core/tasks.fsi @@ -237,7 +237,7 @@ module LowPriority = /// member inline Using: resource: 'Resource * body: ('Resource -> TaskCode<'TOverall, 'T>) -> TaskCode<'TOverall, 'T> - when 'Resource :> IDisposable + when 'Resource :> IDisposable|null /// /// Contains medium-priority overloads for the `task` computation expression builder.