Skip to content
5 changes: 5 additions & 0 deletions .fantomasignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
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 @@ -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))
3 changes: 2 additions & 1 deletion docs/release-notes/.FSharp.Core/9.0.300.md
Original file line number Diff line number Diff line change
@@ -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.
8 changes: 4 additions & 4 deletions src/Compiler/Utilities/Activity.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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[]

Expand Down
7 changes: 4 additions & 3 deletions src/Compiler/Utilities/Activity.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 =
Expand Down
2 changes: 1 addition & 1 deletion src/Compiler/Utilities/Cancellable.fs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ type CancellableBuilder() =
| Choice2Of2 err -> Cancellable.run ct (handler err)
| ValueOrCancelled.Cancelled err1 -> ValueOrCancelled.Cancelled err1)

member inline _.Using(resource, [<InlineIfLambda>] comp) =
member inline _.Using(resource: _ MaybeNull, [<InlineIfLambda>] comp) =
Cancellable(fun ct ->
#if !FSHARPCORE_USE_PACKAGE
__debugPoint ""
Expand Down
6 changes: 5 additions & 1 deletion src/Compiler/Utilities/Cancellable.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,12 @@ type internal CancellableBuilder =
comp: Cancellable<'T> * [<InlineIfLambda>] handler: (exn -> Cancellable<'T>) -> Cancellable<'T>

member inline Using:
resource: 'Resource * [<InlineIfLambda>] comp: ('Resource -> Cancellable<'T>) -> Cancellable<'T>
resource: 'Resource MaybeNull * [<InlineIfLambda>] 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<unit>

Expand Down
5 changes: 4 additions & 1 deletion src/Compiler/Utilities/lib.fs
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,10 @@ type DisposablesTracker() =
let items = Stack<IDisposable>()

/// 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

Expand Down
7 changes: 6 additions & 1 deletion src/Compiler/Utilities/lib.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion src/FSharp.Core/async.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion src/FSharp.Core/async.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -1325,7 +1325,7 @@ namespace Microsoft.FSharp.Control
/// <returns>An asynchronous computation that binds and eventually disposes <c>resource</c>.</returns>
///
/// <example-tbd></example-tbd>
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

/// <summary>Creates an asynchronous computation that runs <c>computation</c>, and when
/// <c>computation</c> generates a result <c>T</c>, runs <c>binder res</c>.</summary>
Expand Down
5 changes: 4 additions & 1 deletion src/FSharp.Core/prim-types.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
4 changes: 4 additions & 0 deletions src/FSharp.Core/prim-types.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -1796,7 +1796,11 @@ namespace Microsoft.FSharp.Core

/// <summary>A compiler intrinsic for the efficient compilation of sequence expressions</summary>
[<CompilerMessage("This function is for use by compiled F# code and should not be used directly", 1204, IsHidden=true)>]
#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

/// <summary>A compiler intrinsic for checking initialization soundness of recursive bindings</summary>
[<CompilerMessage("This function is for use by compiled F# code and should not be used directly", 1204, IsHidden=true)>]
Expand Down
2 changes: 1 addition & 1 deletion src/FSharp.Core/resumable.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand Down
2 changes: 1 addition & 1 deletion src/FSharp.Core/resumable.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -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: [<InlineIfLambda>] condition: (unit -> bool) * body: ResumableCode<'Data, unit> -> ResumableCode<'Data, unit>
Expand Down
4 changes: 2 additions & 2 deletions src/FSharp.Core/tasks.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
2 changes: 1 addition & 1 deletion src/FSharp.Core/tasks.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ module LowPriority =
/// </summary>
member inline Using:
resource: 'Resource * body: ('Resource -> TaskCode<'TOverall, 'T>) -> TaskCode<'TOverall, 'T>
when 'Resource :> IDisposable
when 'Resource :> IDisposable|null

/// <summary>
/// Contains medium-priority overloads for the `task` computation expression builder.
Expand Down