114

Compiled with VS 2012, with project type WP 8.0 the following code will fail if debugger is not attached.

Somehow, if debugger not attached, compiler optimizations ruins the code inside Crash() - see comments in code.

Tested on Lumia 1520 (8.1) and Lumia 630 (8.0).

Any ideas why this is occurring?

public partial class MainPage : PhoneApplicationPage
{
    public MainPage()
    {
        InitializeComponent();
        Button.Tap += (sender, args) => new A<B, string>(new B(), "string").Crash();
    }
}
public class B
{
    public void Foo<T>(T val) { }
}
public class A<T1, T2> where T1 : B
{
    private T1 _t1;
    private T2 _t2;
    public A(T1 t1, T2 t2)
    {
        _t2 = t2;
        _t1 = t1;
    }
    public void Crash()
    {
        var obs = Observable.Return(_t2);
        obs.Subscribe(result =>
        {
            //CLR is expecting T2 to be System.String here,
            //but somehow, after passing through Observable
            //T2 here is not a string, it's A<T1, T2>

            new List<T2>().Add(result);
        });
        //Will run normally if commented
        _t1.Foo(new object());
    }
}
Steve Lillis
  • 3,263
  • 5
  • 22
  • 41
Yuriy Naydenov
  • 1,875
  • 1
  • 13
  • 31
  • 6
    Seems like a compiler bug, not an Rx bug. Have you tried using ILSpy or .NET Reflector to examine the generated IL? – Brandon Jul 08 '14 at 00:01
  • 8
    I would try using `Observable.Return(_t2);`, rather than leaving it up to the compiler to decide the type here. There might be a bug with that. Granted, that's a long shot. – cwharris Jul 18 '14 at 01:52
  • 6
    I've had a ton of problems with Rx on Windows Phone. For me, it would compile, then throw a `MethodNotFoundException` when I actually tried to call the containing class. For me, upgrading to the release version of VS Update 2 worked. I still have no idea what was actually wrong, but make sure you're using the latest updates on everything. Obviously our problems are a little different, but that might help lend some guidance. – Matthew Haugen Jul 20 '14 at 23:37
  • What do you mean exactly by "it's A"? I only ask because you wrote "T1, T2" rather than "B, string". I would expect a compiler or CLR bug that mixes up types such as this to at least identify A as a _bound_ generic type. It would seem to be unverifiable IL either way, which I would think should also cause a different error. So what gave you that idea? – Dave Sexton Aug 30 '14 at 11:43
  • 5
    What is the question - 'any ideas?' - do you just want to know how to get it to stop crashing? – Tim Lovell-Smith Feb 24 '15 at 17:20
  • 1
    may be because _t1.Foo(and here too); – akbar ali Feb 26 '15 at 06:17
  • This seems like a compiler bug. The problematic line is the `new List().Add(result);` . The problem is that the compiler should ideally be creating the delegate for the subscription in the scope of the generic class. But since, the compiler detects that you're not using any of the local variables, and figures it could be lifted it all the way up. Except, it forgot about the GenericTypeDefinition! So, your delegate that's the optimization creates is a generic definition, instead of a constructed type. You should probably file the bug at connect. –  Feb 26 '15 at 23:15
  • It would probably be more helpful if you posted the IL generated. Also, out of curiosity, does this also happen if you change your event handler from `Button.Tap += (sender, args) => new A(new B(), "string").Crash();` to `Button.Tap += (sender, args) => { var x = new A(new B(), "string"); x.Crash(); } ` –  Feb 26 '15 at 23:18
  • @Yuriy Please post the Debug and Release IL, if possible. – Adam Houldsworth May 28 '15 at 12:02
  • 1
    Akbar Ali is right - the .Foo() call is not parametrized, which forces it to infer the type. You cannot expect it to know it is , provided the parameter you pass is object. – Kamen Jun 04 '15 at 08:25

1 Answers1

1
 _t1.Foo<type>(type);

You are missing the type declaration. The compiler is guessing (and guessing wrong). Strictly type everything and it should run.

Japes
  • 255
  • 2
  • 16
  • This is not a clue, you can implement IObserver and IObservable by yourself and everything will work just fine. – Yuriy Naydenov Jun 16 '15 at 09:10
  • It looks like the debugger is creating a connection to the compiler, and the debugger as well needs all vars to be strictly typed. The debugger is guessing correct, and the compiler is somehow taking ques from it. It doesnt really matter why the debugger fixes the problem, the root cause has been found. – Japes Jun 18 '15 at 03:54