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
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:
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.
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
Deconstructcalls.Repro
Oneround-trips.TwoBackToBackdecompiles to: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).DeconstructionTransformmatches a deconstruction againstlocals 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 Releaseon the standalone case above, and inDeconstructionTestson alleight 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.