#4059·ILSpy

Two back-to-back deconstructions in one method are not recognized

Author: siegfriedpammerCreated Aug 26, 2026Updated Sep 4, 2026
LabelsBugDecompiler

Two deconstructions of the same source type in one method are not recognized: the first one alone decompiles correctly, but as soon as a second follows, both fall back to raw Deconstruct calls.

Repro

csharp
public class Src<T, T2>
{
    public void Deconstruct(out T a, out T2 b) { a = default; b = default; }
}

public class C
{
    private static Src<string, string> GetSource() => null;

    public static void One()
    {
        var (value, value2) = GetSource();
        Console.WriteLine(value);
        Console.WriteLine(value2);
    }

    public static void TwoBackToBack()
    {
        var (value, value2) = GetSource();
        Console.WriteLine(value);
        Console.WriteLine(value2);
        var (value3, value4) = GetSource();
        Console.WriteLine(value3);
        Console.WriteLine(value4);
    }
}

One round-trips. TwoBackToBack decompiles to:

csharp
public static void TwoBackToBack()
{
    GetSource().Deconstruct(out var a, out var b);
    string value = a;
    string value2 = b;
    Console.WriteLine(value);
    Console.WriteLine(value2);
    GetSource().Deconstruct(out b, out a);   // note: same locals, swapped
    string value3 = b;
    string value4 = a;
    Console.WriteLine(value3);
    Console.WriteLine(value4);
}

Why

csc reuses the same two out-slot temporaries for both calls, and the second call receives them in the opposite order (out b, out a). DeconstructionTransform matches a deconstruction against locals that the pattern owns; locals already written by an earlier deconstruction in the same method do not satisfy that, so neither call is recognized.

Splitting the shared out-slot temporaries into per-deconstruction locals before pattern matching should let both match independently.

Scope

Reproduced with -c Release on the standalone case above, and in DeconstructionTests on all eight compiler configurations the fixture runs (Roslyn 2.10, 3.11, 4.14 and latest, with and without Optimize), so it is not compiler-version specific.

Found while porting shape coverage onto DeconstructionTests: 19 of 20 additional shapes pass, this is the one that does not. The test case is checked in commented out, referencing this issue.

Written by an AI agent (Claude) on Siegfried's behalf.