Skip to content

Pattern matching emits invalid IL when input type is a value type and extension Deconstruct's this parameter is a reference type #81173

@AlekseyTs

Description

@AlekseyTs
class Program
{
    static void Main()
    {
        System.Console.Write(Test2(new C()));
    }

    static bool Test2(C u)
    {
        return u is var (_ , (i, _)) && (int)i == 10;
    }   

    static void Test3(C c, int i)
    {
        var (a, b) = c;
        var (u, v) = i;
    }   
}

public struct C
{
}

static class Extensions
{
    public static void Deconstruct(this object o, out int x, out int y)
    {
        x = 10;
        y = 2;
    }
}

PEVerify output:

Microsoft (R) .NET Framework PE Verifier.  Version  4.0.30319.0
Copyright (c) Microsoft Corporation.  All rights reserved.

[IL]: Error: [Program::Test2][offset 0x00000006][found value 'C'][expected ref 'System.Object'] Unexpected type on the stack.
[IL]: Error: [Program::Test2][offset 0x00000011][found Int32][expected ref 'System.Object'] Unexpected type on the stack.
2 Error(s)

Here is IL for Test2 (note value type receivers for Deconstruct aren't boxed):

.method private hidebysig static bool  Test2(valuetype C u) cil managed
{
  // Code size       35 (0x23)
  .maxstack  3
  .locals init (int32 V_0,
           int32 V_1,
           int32 V_2,
           int32 V_3,
           bool V_4)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldloca.s   V_1
  IL_0004:  ldloca.s   V_2
  IL_0006:  call       void Extensions::Deconstruct(object,
                                                    int32&,
                                                    int32&)
  IL_000b:  nop
  IL_000c:  ldloc.2
  IL_000d:  ldloca.s   V_0
  IL_000f:  ldloca.s   V_3
  IL_0011:  call       void Extensions::Deconstruct(object,
                                                    int32&,
                                                    int32&)
  IL_0016:  nop
  IL_0017:  ldloc.0
  IL_0018:  ldc.i4.s   10
  IL_001a:  ceq
  IL_001c:  stloc.s    V_4
  IL_001e:  br.s       IL_0020
  IL_0020:  ldloc.s    V_4
  IL_0022:  ret
} // end of method Program::Test2

It looks like regular Deconstruction emits correct IL (i.e. with boxing for value type receivers):

.method private hidebysig static void  Test3(valuetype C c,
                                             int32 i) cil managed
{
  // Code size       46 (0x2e)
  .maxstack  3
  .locals init (int32 V_0,
           int32 V_1,
           int32 V_2,
           int32 V_3,
           int32 V_4,
           int32 V_5)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  box        C
  IL_0007:  ldloca.s   V_4
  IL_0009:  ldloca.s   V_5
  IL_000b:  call       void Extensions::Deconstruct(object,
                                                    int32&,
                                                    int32&)
  IL_0010:  nop
  IL_0011:  ldloc.s    V_4
  IL_0013:  stloc.0
  IL_0014:  ldloc.s    V_5
  IL_0016:  stloc.1
  IL_0017:  ldarg.1
  IL_0018:  box        [mscorlib]System.Int32
  IL_001d:  ldloca.s   V_5
  IL_001f:  ldloca.s   V_4
  IL_0021:  call       void Extensions::Deconstruct(object,
                                                    int32&,
                                                    int32&)
  IL_0026:  nop
  IL_0027:  ldloc.s    V_5
  IL_0029:  stloc.2
  IL_002a:  ldloc.s    V_4
  IL_002c:  stloc.3
  IL_002d:  ret
} // end of method Program::Test3

Should also check other scenarios where pattern matching can use extension methods/properties.

Metadata

Metadata

Assignees

Type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions