Skip to content

Suboptimal code is generated for evaluation of list patterns, could be an artifact of used subsumption checking strategy #57731

@AlekseyTs

Description

@AlekseyTs

Example 1:

    static int Test1(int[] x)
    {
        switch (x)
        {
            case [.., 1] and [1, ..]: return 0;
        }
        
        return 1;
    }

IL decompiled into C# looks like this:

    private static int Test1(int[] x)
    {
        if (x != null)
        {
            int length = x.Length;
            if (length >= 1 && x[length - 1] == 1)
            {
                int num = x[0];
                if (length == 1 || num == 1)
                {
                    return 0;
                }
            }
        }
        return 1;
    }

I think that the way length == 1 is placed makes it redundant. It looks to me that the code would be more optimal if the check wasn't there at all, or x[0] was evaluated only when length == 1 is false.
Option 1:

    private static int Test1(int[] x)
    {
        if (x != null)
        {
            int length = x.Length;
            if (length >= 1 && x[length - 1] == 1)
            {
                if (x[0] == 1)
                {
                    return 0;
                }
            }
        }
        return 1;
    }

Option 2:

    private static int Test1(int[] x)
    {
        if (x != null)
        {
            int length = x.Length;
            if (length >= 1 && x[length - 1] == 1)
            {
                if (length == 1 || x[0] == 1)
                {
                    return 0;
                }
            }
        }
        return 1;
    }

Example 2:

    static int Test1(int[] x)
    {
        switch (x)
        {
            case [2, ..] and [.., 1]: return 0;
        }
        
        return 3;
    }

Decompiled code:

    private static int Test1(int[] x)
    {
        if (x != null)
        {
            int length = x.Length;
            if (length >= 1 && x[0] == 2)
            {
                int num = x[length - 1];
                if (length != 1 && num == 1)
                {
                    return 0;
                }
            }
        }
        return 3;
    }

Basically it has the similar issue.

Example 3:

    static int Test1(int[] x)
    {
        switch (x)
        {
            case [2, ..]: return 4;
            case [.., 1]: return 5;
        }
        
        return 3;
    }

Decompiled code:

    private static int Test1(int[] x)
    {
        if (x != null)
        {
            int length = x.Length;
            if (length >= 1)
            {
                if (x[0] == 2)
                {
                    return 4;
                }
                int num = x[length - 1];
                int num2 = 1;
                if (num == 1)
                {
                    return 5;
                }
            }
        }
        return 3;
    }

There is an unnecessary int num2 = 1;

Example 4:

    static int Test1(int[] x)
    {
        switch (x)
        {
            case [2, ..]: return 4;
            case [.., 1]: return 5;
            case [6,..,7]: return 8;
        }
        
        return 3;
    }

Decompiled code:

    private static int Test1(int[] x)
    {
        if (x != null)
        {
            int length = x.Length;
            if (length >= 1)
            {
                int num = x[0];
                if (num == 2)
                {
                    return 4;
                }
                int num2 = x[length - 1];
                if (length == 1)
                {
                    if (num2 == 1)
                    {
                        goto IL_0036;
                    }
                }
                else
                {
                    if (num2 == 1)
                    {
                        goto IL_0036;
                    }
                    if (num == 6 && num2 == 7)
                    {
                        return 8;
                    }
                }
            }
        }
        return 3;
        IL_0036:
        return 5;
    }

Note that the following `if`` is duplicated:

                    if (num2 == 1)
                    {
                        goto IL_0036;
                    }

Also, similar to the first example, checking length == 1 after evaluating x[length - 1] isn't that helpful.

Option 1:

    private static int Test1(int[] x)
    {
        if (x != null)
        {
            int length = x.Length;
            if (length >= 1)
            {
                int num = x[0];
                if (num == 2)
                {
                    return 4;
                }
                int num2 = x[length - 1];
                if (num2 == 1)
                {
                    return 5;
                }
                if (num == 6 && num2 == 7)
                {
                    return 8;
                }
            }
        }
        return 3;
    }

Option 2:

    private static int Test1(int[] x)
    {
        if (x != null)
        {
            int length = x.Length;
            if (length >= 1)
            {
                int num = x[0];
                if (num == 2)
                {
                    return 4;
                }
                if (length == 1)
                {
                    if (num == 1)
                    {
                        goto IL_0036;
                    }
                }
                else
                {
                    int num2 = x[length - 1];
                    if (num2 == 1)
                    {
                        goto IL_0036;
                    }
                    if (num == 6 && num2 == 7)
                    {
                        return 8;
                    }
                }
            }
        }
        return 3;
        IL_0036:
        return 5;
    }

It is quite possible the more complicated scenario becomes, the more questionable artifacts the generated code is going to have.

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions