Skip to content

dup instruction interferes with collection initialization for value types #3552

@ds5678

Description

@ds5678

Input code

.class public auto ansi abstract sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552
    extends [System.Runtime]System.Object
{
    // Methods
    .method public hidebysig static 
        valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair MakePair1 (
            int32 x,
            int32 y
        ) cil managed 
    {
        // Method begins at RVA 0x2050
        // Header size: 12
        // Code size: 34 (0x22)
        .maxstack 2
        .locals init (
            [0] valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder
        )

        IL_0000: ldloca.s 0
        IL_0002: initobj ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder
        IL_0008: ldloca.s 0
        IL_000a: ldarg.0
        IL_000b: call instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::Add(int32)
        IL_0010: ldloca.s 0
        IL_0012: ldarg.1
        IL_0013: call instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::Add(int32)
        IL_0015: ldloca.s 0
        IL_001c: call instance valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::ToPair()
        IL_0021: ret
    } // end of method Issue3552::MakePair1

    .method public hidebysig static 
        valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair MakePair2 (
            int32 x,
            int32 y
        ) cil managed 
    {
        // Method begins at RVA 0x2050
        // Header size: 12
        // Code size: 34 (0x22)
        .maxstack 2
        .locals init (
            [0] valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder
        )

        IL_0000: ldloca.s 0
        IL_0001: dup
        IL_0002: initobj ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder
        IL_0008: dup
        IL_000a: ldarg.0
        IL_000b: call instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::Add(int32)
        IL_0012: ldarg.1
        IL_0013: call instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::Add(int32)
        IL_0015: ldloca.s 0
        IL_001c: call instance valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::ToPair()
        IL_0021: ret
    } // end of method Issue3552::MakePair2

    .method public hidebysig static 
        valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair MakePair3 (
            int32 x,
            int32 y
        ) cil managed 
    {
        // Method begins at RVA 0x2050
        // Header size: 12
        // Code size: 34 (0x22)
        .maxstack 2
        .locals init (
            [0] valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder
        )

        IL_0000: ldloca.s 0
        IL_0001: dup
        IL_0002: initobj ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder
        IL_0008: dup
        IL_000a: ldarg.0
        IL_000b: call instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::Add(int32)
        IL_0010: dup
        IL_0012: ldarg.1
        IL_0013: call instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::Add(int32)
        IL_001c: call instance valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::ToPair()
        IL_0021: ret
    } // end of method Issue3552::MakePair3

} // end of class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552

.class public sequential ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair
    extends [System.Runtime]System.ValueType
{
    // Fields
    .field public int32 X
    .field public int32 Y

} // end of class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair

.class public sequential ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder
    extends [System.Runtime]System.ValueType
    implements class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32>,
               [System.Runtime]System.Collections.IEnumerable
{
    // Fields
    .field private int32 index
    .field private valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair pair

    // Methods
    .method public hidebysig 
        instance valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair ToPair () cil managed 
    {
        .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (
            01 00 00 00
        )
        // Method begins at RVA 0x207e
        // Header size: 1
        // Code size: 7 (0x7)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: ldfld valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::pair
        IL_0006: ret
    } // end of method Issue3552_IntegerPairBuilder::ToPair

    .method public hidebysig 
        instance void Add (
            int32 'value'
        ) cil managed 
    {
        // Method begins at RVA 0x2088
        // Header size: 12
        // Code size: 65 (0x41)
        .maxstack 3
        .locals init (
            [0] int32
        )

        IL_0000: ldarg.0
        IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::index
        IL_0006: stloc.0
        IL_0007: ldloc.0
        IL_0008: brfalse.s IL_0010

        IL_000a: ldloc.0
        IL_000b: ldc.i4.1
        IL_000c: beq.s IL_001e

        IL_000e: br.s IL_002c

        IL_0010: ldarg.0
        IL_0011: ldflda valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::pair
        IL_0016: ldarg.1
        IL_0017: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair::X
        IL_001c: br.s IL_0032

        IL_001e: ldarg.0
        IL_001f: ldflda valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::pair
        IL_0024: ldarg.1
        IL_0025: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPair::Y
        IL_002a: br.s IL_0032

        IL_002c: newobj instance void [System.Runtime]System.IndexOutOfRangeException::.ctor()
        IL_0031: throw

        IL_0032: ldarg.0
        IL_0033: ldarg.0
        IL_0034: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::index
        IL_0039: ldc.i4.1
        IL_003a: add
        IL_003b: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::index
        IL_0040: ret
    } // end of method Issue3552_IntegerPairBuilder::Add

    .method public final hidebysig newslot virtual 
        instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<int32> GetEnumerator () cil managed 
    {
        // Method begins at RVA 0x20d5
        // Header size: 1
        // Code size: 2 (0x2)
        .maxstack 8

        IL_0000: ldnull
        IL_0001: ret
    } // end of method Issue3552_IntegerPairBuilder::GetEnumerator

    .method private final hidebysig newslot virtual 
        instance class [System.Runtime]System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () cil managed 
    {
        .override method instance class [System.Runtime]System.Collections.IEnumerator [System.Runtime]System.Collections.IEnumerable::GetEnumerator()
        // Method begins at RVA 0x20d8
        // Header size: 1
        // Code size: 7 (0x7)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: call instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<int32> ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder::GetEnumerator()
        IL_0006: ret
    } // end of method Issue3552_IntegerPairBuilder::System.Collections.IEnumerable.GetEnumerator

} // end of class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.Issue3552_IntegerPairBuilder

Based on:

https://lab.razor.fyi/#nVTNbhMxEBaIA7gXxBOM1EurolWQOGUJEoSqCgI1antoucDEO0msOvZqbCdZVXkK3gDxYlz7EiBvSDY_m6AyB3vX_uYbz8xnix8HQnTZDhhHiXQvvh8cNnO2ObEvoG1NXw0Co1fWtC5IEzoSwSkzgMvCeRqla39J22pNMqLd7p3kjAyxkqk4NEFr7GmCTLk4C4MjcjlKgk77coict21GyQeSdpQrTZxckfOuHNvoyCWdT10m7wtxJwAA8tDTSoLz6JUEqdE56DgX6HWj0SgRc9w2don62jGeBsRdVAyf8Zbix5EyHqYvIU7F8ZKiIotWS_E-KJ0Rg9q32QJDk_3-dzF8AbN0LSSTD2z2kidXtszguPKciWpcVoGD9PVHqK9bLMV1Wrd6kz6Mf5FiEzqnJoyIoxTeKOPfbkZmNUZPZRBlMpqmW1v1bcxRcSo2z8qEmTW62OG0qNuObv8t_Zy6quxmkLFVGbzLslJBY9SBdhG6ifJyCEdlZhVoGxhNoiNoNLfWy8ioOLmG1jzcumAW1mPC27Se9tUe2pv_os2oj0H7el4_ZDuZX4CY-Xnw5_0LNAM6nUrK45Oxql1YKmthZb1OTvY1YaErb7nUFZyRr5b-1eL4StWyr9CuSjd5EPsGePuWzsTHx0p_efb0_tfv-5_950--PfoD

I optimized MakePair in 3 ways.

Erroneous output

// Actual
public static Issue3552_IntegerPair MakePair1(int x, int y)
{
	Issue3552_IntegerPairBuilder issue3552_IntegerPairBuilder = new Issue3552_IntegerPairBuilder { x, y };
	return issue3552_IntegerPairBuilder.ToPair();
}
public static Issue3552_IntegerPair MakePair2(int x, int y)
{
	Issue3552_IntegerPairBuilder issue3552_IntegerPairBuilder = default(Issue3552_IntegerPairBuilder);
	issue3552_IntegerPairBuilder.Add(x);
	issue3552_IntegerPairBuilder.Add(y);
	return issue3552_IntegerPairBuilder.ToPair();
}
public static Issue3552_IntegerPair MakePair3(int x, int y)
{
	Issue3552_IntegerPairBuilder issue3552_IntegerPairBuilder = default(Issue3552_IntegerPairBuilder);
	issue3552_IntegerPairBuilder.Add(x);
	issue3552_IntegerPairBuilder.Add(y);
	return issue3552_IntegerPairBuilder.ToPair();
}

// Expected
public static Issue3552_IntegerPair MakePair1(int x, int y)
{
	Issue3552_IntegerPairBuilder issue3552_IntegerPairBuilder = new Issue3552_IntegerPairBuilder { x, y };
	return issue3552_IntegerPairBuilder.ToPair();
}
public static Issue3552_IntegerPair MakePair2(int x, int y)
{
	Issue3552_IntegerPairBuilder issue3552_IntegerPairBuilder = new Issue3552_IntegerPairBuilder { x, y };
	return issue3552_IntegerPairBuilder.ToPair();
}
public static Issue3552_IntegerPair MakePair3(int x, int y)
{
	Issue3552_IntegerPairBuilder issue3552_IntegerPairBuilder = new Issue3552_IntegerPairBuilder { x, y };
	return issue3552_IntegerPairBuilder.ToPair();
}

The actual and expected outputs are semantically equivalent, but it would be prettier if collection initializers were always used.

Details

  • Product in use: ILSpy
  • Version in use: 58bf5ef
  • Any other relevant information to the issue, or your interest in contributing a fix.
    • I'm currently investigating why this is happening.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugDecompilerThe decompiler engine itself

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions