Skip to content

Witness to trait constraint causes throw of TypeInitializationException in Debug build #865

Closed
@abelbraaksma

Description

@abelbraaksma

Description

The following code, redacted to closely match this brilliant idea of polyvariadic functions in F# on StackOverflow.com from Gustavo (not sure about his Github name) throws a TypeInitializationException when run as a Debug build.

Repro steps

Here's a solution with the full repro, run in Debug build to see the issue: FSharpBug#865.zip.

Or: copy the following code in an empty project and run with F5 in a Debug build, it will throw the TypeInitializationException.

// From: http://stackoverflow.com/questions/28243963/how-to-write-a-variadic-function-in-f-emulating-a-similar-haskell-solution/28244413#28244413
type T = T with
    static member        ($) (T, _:int    ) = (+)
    static member        ($) (T, _:decimal) = (+)       // required, if only the prev line is here, type inference will constrain too much

[<AutoOpen>]
module TestT =
    let inline sum (i:'a) (x:'a) :'r = (T $ Unchecked.defaultof<'r>) i x

type T with
    static member inline ($) (T, _:'t-> 'rest) = fun (a:'t) -> (+) a >> sum

module TestT2 =
    let x:int = sum 2 3 
    let y:int = sum 2 3 4       // this line throws TypeInitializationException in Debug build

[<EntryPoint>]
let main argv = 
    0

Expected

No exceptions expected.

Actual

Throws TypeInitializationException and debugger shows assembler view:

The type initializer for '<StartupCode$Experiments>.$Program' threw an exception.

The stacktrace is empty, the assembler view is unfortunate (if such exceptions rise I'd kind of expect the actual line to be hit or visible in the stacktrace) . Placing a breakpoint right before let y shows that that's the line that triggers the exception in the startup code.

Severity

Possibly high, because there should not be a difference in executing a debug build vs a release build, and certainly this code should not throw a TypeInitializationException.

Version

Tested with VS2015 (14.0.23107.0) and VS2012. It fails at least in these version combinations:

  • FSharp.Core 4.3.0 (4.03.50727.0)
  • FSharp.Core 4.3.1 (4.31.30826.0)
  • FSharp.Core 4.4 (4.40.23020.0)
  • with .NET 4.5, 4.5.1, 4.5.2, 4.6
  • with or without RyuJIT enabled

All's tested without Update 1 or 2.

Workarounds

None that I am aware of (though I'd be interested in suggestions).

Other info

I know it is by design that let-bindings are loaded on first use of an assembly, but as seen here this can lead to hard-to-debug errors. Not that late-binding would have helped here, except perhaps for a more interesting stacktrace (I was quite surprised to find the error being raised without anything in the main function and with an empty stacktrace, until it hit me).

Additional info (update)

I noticed that, using Historical Debugging, prior to the TypeInitializationException, a NotSupportedException was thrown. However, the debugger refused to show the disassembly. The NSE was raised on the >> sum part in the code above.

'System.NotSupportedException' in FSharpBug865.exe ("Specified method is not supported.")   

Stacktrace of that exception;

FSharpBug865.exe!op_Dollar@11-4.Invoke(b i = {unknown}, b x = {unknown})
FSharpBug865.exe!<StartupCode$FSharpBug865>.$Program..cctor()
FSharpBug865.exe!Program.main(string[] argv = {unknown})

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugImpact-Medium(Internal MS Team use only) Describes an issue with moderate impact on existing code.Ready

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions