Description
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})