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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions