dotnet / dotnet/fsharp

VerificationException on runtime, due to upcast to obj inside the task state machine

Open
#16,154 4 comments 0 reactions 0 assignees View on GitHub
Area-Compiler-StateMachines Bug Impact-Low Regression
Dominant language
F#
Stars
4.3k
Forks
876
Avg merge
4d 22h
Merged PRs (30d)
144

Description

There I was, minding my own business, of writing generic higher order functions in asynchronous code, when she hit me, the task-state-machine. She has hit me before, but this time I think she left some traces of her, so I could saw a glimpse of that monster. So I'll report it here, even this will probably be closed "as designed", it might help some other guy struggling in seduction by her.

I haven't been able to repro it in short code, yet, but this is what is going on.
Observe the following code:

```fsharp
open System.Linq
type Shape = {x: int; y: int}

let returnFilter condition =
task {
if condition = "a" then
let filter1 : IQueryable -> IQueryable =
fun data -> data.Where(fun a -> a.x = 1)
return Some filter1
elif condition = "b" then
let filter2 : IQueryable -> IQueryable =
fun data -> data.Where(fun a -> a.y <> 2)
return Some filter2
else
return None
}
```
...and then composing these filters back and forth, e.g.
```fsharp
let data = [{x = 1; y = 1}; {x = 2; y = 2}].AsQueryable()
task {
let! getfilter1 = returnFilter "a"
let! getfilter2 = returnFilter "b"
match getfilter1, getfilter2 with
| Some filter1, Some filter2 ->
return data |> filter2 |> filter1 |> Seq.toList
// ...and so on...
```

**Expected behavior**

Silly me, by the code I expected the type of `returnFilter` is:
`string -> Task<(IQueryable -> IQueryable) option>`

**Actual behavior**

Well, the actual type of `returnFilter` is:
`string -> Task<(#IQueryable -> IQueryable) option>`

The code compiled well.
But this hash *#* made all the difference, because combining different returnFilters together caused them to be automatically upcasted to `obj`.

Thus the code threw at runtime:
```
System.Security.VerificationException: Method returnFilter: type argument 'System.Object' violates the constraint of type parameter 'a'.
at ...(my code location of "task{")...
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.Start[TStateMachine](TStateMachine& stateMachine)
```

What I historically do is change the code from `task` to `async` and all is good, but not this time:

```
System.Security.VerificationException: Method returnFilter: type argument 'System.Object' violates the constraint of type parameter 'a'.
at Microsoft.FSharp.Control.AsyncPrimitives.CallThenInvoke[T,TResult](AsyncActivation`1 ctxt, TResult result1, FSharpFunc`2 part2) in D:\\a\\_work\\1\\s\\src\\FSharp.Core\\async.fs:line 510
at ...(mycode)...
at Microsoft.FSharp.Control.Trampoline.Execute(FSharpFunc`2 firstAction) in D:\\a\\_work\\1\\s\\src\\FSharp.Core\\async.fs:line 114
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at ...(mycode)...
```

**Known workarounds**

Do write the parameters explicitly.
You can even declare a type for your function:

```fsharp
type ShapeFilter = IQueryable -> IQueryable
let returnFilter condition : Task>=
//... and now the compiler is happy, and the runtime is happy.
```

**Related information**

Provide any related information (optional):

* Operating system Windows 11
* .NET Runtime kind: dotnet 8.0.100-rc.1.23463.5, .NET 4.8.09032
* Editing Tools Visual Studio 2022 Version 17.7.4

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.