Closed
Description
In this SO question the minimum example of a bug in Debug mode.
This code works perfectly in Release mode, but throws in Debug mode with:
System.TypeLoadException : The generic type 'System.Tuple`2' was used with an invalid instantiation in assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.
open System
open System.Collections.Generic
open System.Runtime.InteropServices
module Test =
type MyType<'A,'B,'C>() =
let mutable counter = 0
member this.Counter with get() = counter and set(v) = counter <- v
member inline this.TryDoWorkChecked(next:KeyValuePair<'A,'B>, [<Out>] value: byref<'C>) : bool =
let before = this.Counter
let res = this.TryDoWork(next, &value)
if before <> this.Counter then raise (InvalidOperationException("Must not change counter during work"))
else res
abstract TryDoWork: next:KeyValuePair<'A,'B> * [<Out>] value: byref<'C> -> bool
override this.TryDoWork(next:KeyValuePair<'A,'B>, [<Out>] value: byref<'C>) : bool =
value <- Unchecked.defaultof<'C>
if counter > 10 then true else false
member this.DoWork(next:KeyValuePair<'A,'B>) =
let mutable value = Unchecked.defaultof<'C>
while not (this.TryDoWorkChecked(next, &value)) do
counter <- counter + 1
open Test
[<EntryPoint>]
let main argv =
let myType = MyType<int,int,int>()
let next = Unchecked.defaultof<KeyValuePair<int,int>>
myType.DoWork(next)
Console.ReadLine() |> ignore
0
In Debug mode, ILSpy shows this:
// Program.Test.MyType<A, B, C>
public unsafe void DoWork(KeyValuePair<A, B> next)
{
C value = default(C);
while (true)
{
Tuple<KeyValuePair<A, B>, C*> tuple = new Tuple<KeyValuePair<A, B>, C*>(next, ref value);
KeyValuePair<A, B> item = tuple.Item1;
C* item2 = tuple.Item2;
int num = this.Counter;
bool flag = this.TryDoWork(item, item2);
if ((num == this.Counter) ? flag : Operators.Raise<bool>(new InvalidOperationException("Must not change counter during work")))
{
break;
}
this.counter++;
}
}
And this is ILSpy output in Release mode:
// Program.Test.MyType<A, B, C>
public void DoWork(KeyValuePair<A, B> next)
{
C value = default(C);
while (true)
{
int num = this.counter;
bool flag = this.TryDoWork(next, out value);
if (num != this.counter)
{
break;
}
if (flag)
{
return;
}
this.counter++;
}
throw new InvalidOperationException("Must not change counter during work");
}