dotnet / dotnet/fsharp

TypeLoadException using CE applicatives and AOT

Open
#15,488 1 comment 1 reaction 0 assignees View on GitHub
Area-AOT Bug Impact-Medium
Dominant language
F#
Stars
4.3k
Forks
876
Avg merge
4d 22h
Merged PRs (30d)
144

Description

Please provide a succinct description of the issue.

When using applicatives CE (let! and!) with 6 or more bindings, the code generated fails one compiled with AOT. Everything is fine with 5 bindings or less.

Provide the steps required to reproduce the problem:

Implement a Computation Expression with applicative and use it with 6 or more bindings:

```fsharp
type Identity<'t> = Identity of 't

let ret x = Identity x
let map f (Identity x) = Identity (f x)
let map2 f (Identity x) (Identity y) = Identity(f x y)

type IdentityBuilder() =
member _.Return(x) = ret x
member _.BindReturn(x, f) = map f x
member _.MergeSources(x,y) = map2 (fun a b -> a,b) x y

let ident = IdentityBuilder()

[]
let main (args: string[]) =
let (Identity(a,b,c,d,e,f)) =
ident {
let! a = ret "a"
and! b = ret "b"
and! c = ret "c"
and! d = ret "d"
and! e = ret "e"
and! f = ret "f"

return a,b,c,d,e,f

}

printfn $"{a}{b}{c}{d}{e}{f}"
0
```
Here this is a simple Identity functor implementing `map2`.

Compile it with AOT:
```
dotnet publish -c Release -p:SelfContained=true -p:PublishAot=true -o .\dist\
```
And run it. It fails:
```
Unhandled Exception: System.TypeLoadException: Attempted to load a type that was not created during ahead of time compilation.
at Internal.Runtime.CompilerHelpers.ThrowHelpers.ThrowUnavailableType() + 0x33
at Microsoft.FSharp.Core.FSharpFunc`2.InvokeFast[V](FSharpFunc`2, T, TResult) + 0x1a
at Program.main(String[]) + 0x227
at tstconsole!+0x1c779b
```

**Expected behavior**

The application should print "abcdef".

**Actual behavior**

The application fails at runtime with a `TypeLoadException`.

**Known workarounds**

It is possible to workaround by splitting the bindings in smaller chunks:
```fsharp
let (Identity(a,b,c,d,e,f)) =
ident {
let! a = ret "a"
and! b = ret "b"
and! c = ret "c"
and! (d,e,f) =
ident {
let! d = ret "d"
and! e = ret "e"
and! f = ret "f"
return d,e,f
}
return a,b,c,d,e,f
}
```

**Related information**

I discovered this while working on an applicative command line argument parser that is not using reflection to make it work nicely with AOT. However, this bug - while having a workaround - makes it likely to break weirdly when the code is modified.

The way CE works should not produce dynamically loaded types that are not compatible with AOT.

* Operating system: Windows / Linux
* .NET Runtime kind: net6.0+
* Editing Tools: VS / VSCode

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.