Skip to content

Custom CE based on task: Use method in builder lifts resource type to IAsyncDisposable instead of IDisposable on net6 #12563

@melanore

Description

@melanore

Reproduce can be found in this repo - https://github.com/melanore/FSharp6.Tasks.BugReport/blob/master/FSharp6.Tasks.BugReport.TransactionScope/TaskResult.fs

Given custom taskResult builder below:

module Net6.TaskResult

open System
open System.Threading.Tasks

type TaskResultBuilder() =

    member inline _.Source(task : Task<Result<'a, 'e>>) : Task<Result<'a, 'e>> =
        task

    member inline _.Zero() : Task<Result<unit, 'e>> =
        () |> Result.Ok |> Task.FromResult

    member inline _.Bind(taskResult : Task<Result<'a, 'e>>, binder : 'a -> Task<Result<'b, 'e>>) : Task<Result<'b, 'e>> =
        task {
            match! taskResult with
            | Error e ->
                return Error e
            | Ok d ->
                return! binder d
        }

    member inline _.Delay(generator : unit -> Task<Result<'a, 'e>>) : unit -> Task<Result<'a, 'e>> =
        generator

    member inline _.Using(resource : 'a :> IDisposable, binder : 'a -> Task<Result<'b, 'e>>) : Task<Result<'b, 'e>> =
        task {
            use res = resource
            let! result = binder res
            return result
        }
        
    member inline _.Run(f : unit -> Task<'m>) = f ()
    
let taskResult = TaskResultBuilder()

// Having members as extensions gives them lower priority in
// overload resolution between Task<'a> and Task<Either<'a>>.
[<AutoOpen>]
module TaskResultCEExtensions =
    type TaskResultBuilder with
        member inline _.Source(source : Task<'a>) : Task<Result<'a, 'e>> =
            task {
                let! s = source
                return Result.Ok s
            }

When Using method within taskResult builder depends on use from task - resource type is inferred as IAsyncDisposable. Note, that problem happens only on net6 framework, - if framework is switched to netstandard2.0, - type is properly inferred as IDisposable.
Problem seems to be inside multiple Using overloads within task CE - on net6 platform incorrect overload with IAsyncDisposable has bigger priority.

image

image

Rewrite of Using implementation in taskResult to try..finally is a valid workaround atm.

image

  • Operating system: Windows 10
  • .NET Runtime: 6.0.100

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugImpact-Low(Internal MS Team use only) Describes an issue with limited impact on existing code.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions