Skip to content

TypeLoadException using CE applicatives and AOT #15488

Open

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

Activity

  1. added this to the Backlog milestone on Jun 26, 2023
  2. added
    Impact-Medium(Internal MS Team use only) Describes an issue with moderate impact on existing code.
    Area-AOTEverything related to AOT
    and removed on Jul 3, 2023
  3. added a commit that references this issue on Jun 1, 2024
  4. Numpsy commented on Jul 7, 2024

    @Numpsy

    I tried building this with all the trim analysis warnings enabled, and got some warnings like this:

    ILC : AOT analysis warning IL3054: Microsoft.FSharp.Core.FSharpFunc`2<String,String>.InvokeFast<Tuple`3<String,String,Tuple`3<String,String,Tuple`3<String,String,Tuple`3<S
    tring,String,String>>>>,Tuple`3<String,String,Tuple`3<String,String,Tuple`3<String,String,Tuple`3<String,String,Tuple`3<String,String,String>>>>>>(FSharpFunc`2<String,FSha
    rpFunc`2<String,FSharpFunc`2<Tuple`3<String,String,Tuple`3<String,String,Tuple`3<String,String,Tuple`3<String,String,String>>>>,Tuple`3<String,String,Tuple`3<String,String
    ,Tuple`3<String,String,Tuple`3<String,String,Tuple`3<String,String,String>>>>>>>>,String,String,Tuple`3<String,String,Tuple`3<String,String,Tuple`3<String,String,Tuple`3<S
    tring,String,String>>>>): Generic expansion to 'Microsoft.FSharp.Core.OptimizedClosures.FSharpFunc`4<String,String,Tuple`3<String,String,Tuple`3<String,String,Tuple`3<Stri
    ng,String,Tuple`3<String,String,String>>>>,Tuple`3<String,String,Tuple`3<String,String,Tuple`3<String,String,Tuple`3<String,String,Tuple`3<String,String,String>>>>>>' was
    aborted due to generic recursion. An exception will be thrown at runtime if this codepath is ever reached. Generic recursion also negatively affects compilation speed and
    the size of the compilation output. It is advisable to remove the source of the generic recursion by restructuring the program around the source of recursion. The source o
    f generic recursion might include: 'Microsoft.FSharp.Core.FSharpFunc`2', '<StartupCode$FSharp-Core>.$Prim-types.op_Implicit@3875', '<StartupCode$FSharp-Core>.$Prim-types.o
    p_Implicit@3880-1', '<StartupCode$FSharp-Core>.$Prim-types.op_Implicit@3883-2', '<StartupCode$FSharp-Core>.$Prim-types.op_Implicit@3886-3', '<StartupCode$FSharp-Core>.$Pri
    m-types.FromConverter@3889', '<StartupCode$FSharp-Core>.$Prim-types.ToConverter@3892', 'Microsoft.FSharp.Core.OptimizedClosures.FSharpFunc`3', 'Microsoft.FSharp.Core.Optim
    izedClosures.FSharpFunc`4', 'Microsoft.FSharp.Core.OptimizedClosures.FSharpFunc`5', 'Microsoft.FSharp.Core.OptimizedClosures.FSharpFunc`6', 'Microsoft.FSharp.Core.Optimize
    dClosures.Invoke@3756', 'Microsoft.FSharp.Core.OptimizedClosures.Adapt@3763', 'Microsoft.FSharp.Core.OptimizedClosures.Invoke@3770-1', 'Microsoft.FSharp.Core.OptimizedClos
    ures.Adapt@3779-1', 'Microsoft.FSharp.Core.OptimizedClosures.Adapt@3783-2', 'Microsoft.FSharp.Core.OptimizedClosures.Adapt@3797-3', 'Microsoft.FSharp.Core.OptimizedClosure
    s.Adapt@3802-4', 'Microsoft.FSharp.Core.OptimizedClosures.Adapt@3806-5', 'Microsoft.FSharp.Core.OptimizedClosures.Invoke@3809-2', 'Microsoft.FSharp.Core.OptimizedClosures.
    Invoke@3817-3', 'Microsoft.FSharp.Core.OptimizedClosures.Adapt@3827-6', 'Microsoft.FSharp.Core.OptimizedClosures.Adapt@3832-7', 'Microsoft.FSharp.Core.OptimizedClosures.Ad
    apt@3837-8', 'Microsoft.FSharp.Core.OptimizedClosures.Adapt@3841-9' [S:\DevTest\ce_test\ce_test.fsproj]
    

    Which makes it sound like the issue is that there are too many layers of nested tuples and the AOT compiler is giving up?

    A quick test makes it seem like the issue can be alleviated to a degree by implementing MergeSourcesN - e.g. if I add an implementation of MergeSources3 to IdentityBuilder the it works with 10 bindings and fails with 11.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions