runtime: Wrong value passed to generic interface method in release

On .NET core 2.1.0 and .NET framework 4.7.1 the following program prints 0 in debug but 1 in release:

using System;

class C0
{
    public ulong F6;
    public C0(ulong f6)
    {
        F6 = f6;
    }
}

struct S0
{
    public C0 F2;
    public S0(C0 f2)
    {
        F2 = f2;
    }
}

struct S1
{
    public S0 F0;
    public S0 F2;
    public S1(S0 f2)
    {
        F0 = default(S0);
        F2 = f2;
    }
}

interface II0
{
    void WriteLine<T>(T b);
}

class C1 : II0
{
    public void WriteLine<T>(T b)
    {
        Console.WriteLine(b);
    }
}

public class Program
{
    private static II0 s_0;
    public static void Main()
    {
        s_0 = new C1();
        S1[] var0 = {new S1(new S0(new C0(0)))};
        var0[0].F2.F2.F6 = var0[0].F2.F2.F6;
        var0[0].F2.F2 = new C0(1); // The value specified here is printed in release
        var0[0].F2.F2.F6 = 0;
        s_0.WriteLine(var0[0].F2.F2.F6);
    }
}

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 18 (18 by maintainers)

Most upvoted comments

A bit simpler example:

// Debug: Outputs 0
// Release: Outputs 2236837378
struct S1
{
    public uint F0;
    public S1(uint f0): this() { F0 = f0; }
}

struct S2
{
    public S1 F1;
    public S2(S1 f1): this() { F1 = f1; }
}

public class Program
{
    static S2[] s_11 = new S2[]{new S2(new S1(2236837378U))};
    public static void Main()
    {
        ref S1 vr7 = ref s_11[0].F1;
        vr7.F0 = vr7.F0;
        vr7.F0 = 0;
        vr7.F0 = vr7.F0;
        System.Console.WriteLine(vr7.F0);
    }
}

So the CORINFO_HELP_VIRTUAL_FUNC_PTR call inhibits the optimization that makes the result correct otherwise, even though the store is wrong in both cases?

Yes, but

  • it probably should not, I don’t think this particular helper call changes memory in an observable manner
  • as you pretty much found out already, other calls can have the same effect. In general, any call that is not inlined is assumed to change memory in unspecified manner and that blocks CSEing of memory loads across call sites.