Skip to content

TypeLoadException using CE applicatives and AOT #15488

Open
@thinkbeforecoding

Description

@thinkbeforecoding

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:

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()

[<EntryPoint>]
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!<BaseAddress>+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:

    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area-AOTEverything related to AOTBugImpact-Medium(Internal MS Team use only) Describes an issue with moderate impact on existing code.

    Type

    Projects

    Status

    New

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions