Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some edge case in foreach transform #1869

Open
yyjdelete opened this issue Dec 19, 2019 · 2 comments
Open

Some edge case in foreach transform #1869

yyjdelete opened this issue Dec 19, 2019 · 2 comments
Labels
C# Decompiler The decompiler engine itself Enhancement Areas for improvement

Comments

@yyjdelete
Copy link
Contributor

Tested in master
Compile the below code with roslyn and Debug

See the 3 //<-- in the below code.

Also for Span, with the same mode as ForeachArray does.

ForeachArrayA:
https://sharplab.io/#v2:CYLg1APgAgTAjAWAFBQAwAIpwCwG5nJQDMmM6AwugN7Lp2YlTboBiA9gE4CmAhgMYALAIIcOPAJ4AKAJS16NJPSXoAlgDsALgG0Auuh6iJ6ALzo1XAO6rNWuKlQ78i5XQD0rrAE5JBseOlOLnRyLgBmnLyC6JLqGuh8AK4c1vqG/iHKCkHK7r5GpmoJADZFgdn0XpKJHAEZ2ZXVtc4uAL519OEcMZqqJuiouL0APKl+AHQAMlxqAOYaAoNgYCqyzZntLgBuBvFJZKZ54loqjhvKh32FJWXlmHDe1TBNt26uj30D7kMAtN9CRdweMBxOhQioAB5cYApAC2PAAzhouMk2KF0ABJCYAZQADuIzkoGntnkE2mslLk0pdiqUzpVDiS6GSWkA=

        public void ForeachArrayA()
        {
            object[] array = new object[10];//<--temp var for array (int[] array2 = array;) used by foreach should be removed
            foreach (object cur in array)
            {
                array = null;
                Console.WriteLine(cur);
                Console.WriteLine(cur);
            }
            for (int i = 0; i < array.Length; ++i)
            {
                var cur2 = array[i];
                array = null;//<--shouldn't be foreach since array changed during loop(more than one store to var array);
                Console.WriteLine(cur2);
                Console.WriteLine(cur2);
            }
            Console.WriteLine(array);
        }
        public void ForeachArrayE()
        {
            var array = new List<int>();

            foreach (int cur in array)
            {
                array = null;
                Console.WriteLine(cur);
                Console.WriteLine(cur);
            }
            List<int>.Enumerator enumerator = array.GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    int current = enumerator.Current;
                    //enumerator = default;
                    Console.WriteLine(current);
                    current = 0;//<--shouldn'd reuse the same var name as the name of loop var here (DEBUG only)
                    Console.WriteLine(current);
                    Console.WriteLine(current);
                }
            }
            finally
            {
                ((IDisposable)enumerator).Dispose();
            }
            Console.WriteLine(array);
        }
@siegfriedpammer
Copy link
Member

Sorry, but could you please describe the problems in more detail? Maybe it's easier to understand if you split the problems into 3 different pieces of code and show the input, expected output and current output. Thank you very much!

@siegfriedpammer siegfriedpammer added the Feedback Needed This issue requires user feedback label Dec 28, 2019
@yyjdelete
Copy link
Contributor Author

@siegfriedpammer

Case 1

Input

static void Method1()
{
	//shouldn't create another new temp var for foreach
	var array = new int[5];
	foreach(int value in array)//it will use an new temp var copy from `array` if there is other usage of `array`
	{
		//array = null;//<--it will use the orig var instead of temp var
		//Dup usage of `value` to avoid inline
		Console.WriteLine(value);
		Console.WriteLine(value);
	}
	//roslyn will strip the usage of temp var in release mode if `array` is not used in other place
	Console.WriteLine(array);
}

Expected

For Array and maybe also for Span<T>.
ILSpy should also check and treat the temp var(if exists) as a part of foreach(Array) pattern, use the orig var to create the loop, and also remove the temp var when transform to C#
Create an new var for foreach(int value in new int[5]) should be ok, but maybe it's not needed to create another var(array2 in current) for the temp var if there is already an var for the orig one(array in current).

Current

private static void Method1()
{
	int[] array = new int[5];
	int[] array2 = array;
	foreach (int value in array2)
	{
		//array = null;
		Console.WriteLine(value);
		Console.WriteLine(value);
	}
	Console.WriteLine(array);
}

Case 2

Input

static void Method2()
{
	var array = new int[5];
	for (int i = 0; i < array.Length; i++)
	{
		var value = array[i];
		//if the loop var is writen(or access by ref) during loop, it shouldn't be an foreach
		//(modify `array` in really foreach will copy and use an new temp var as loop var, see Method1)
		array = null;
		Console.WriteLine(value);
		Console.WriteLine(value);
	}
}
static void Method2(ref int[] array)
{
	for (int i = 0; i < array.Length; i++)
	{
		var cur = array[i];
		Console.WriteLine(cur);
		Console.WriteLine(cur);
	}
}

Expected

For Array and maybe also for Span<T>
The same as input, since the loop var maybe modified(writen or access by ref) during loop and shouldn't be an foreach

Current

private static void Method2()
{
	int[] array = new int[5];
	foreach (int cur in array)
	{
		array = null;
		Console.WriteLine(cur);
		Console.WriteLine(cur);
	}
}
private static void Method2(ref int[] array)
{
	foreach (int value in array)
	{
		Console.WriteLine(value);
		Console.WriteLine(value);
	}
}

Case 3

Input(Debug only)

//Debug only
static void Method3()
{
	var array = new List<int>();
	
	using(List<int>.Enumerator enumerator = array.GetEnumerator())
	{
		while (enumerator.MoveNext())
		{
			int value = enumerator.Current;
			Console.WriteLine(value);
			Console.WriteLine(value);
			value++;//<--shouldn'd reuse the same var name as the name of loop var here (DEBUG only)
			Console.WriteLine(value);
			Console.WriteLine(value);
		}
	}
}

Expected

Only happen for roslyn+debug, and not for array(only for normal enumerator).
Expected an different name is used for the new var, or the same as input(not transform to foreach).

Current

See the var name conflict for value2

private static void Method3()
{
	List<int> array = new List<int>();
	foreach (int value2 in array)
	{
		Console.WriteLine(value2);
		Console.WriteLine(value2);
		int value2 = value2 + 1;//<--name conflict here
		Console.WriteLine(value2);
		Console.WriteLine(value2);
	}
}

@siegfriedpammer siegfriedpammer added C# Decompiler The decompiler engine itself Enhancement Areas for improvement and removed Feedback Needed This issue requires user feedback labels Mar 3, 2020
ElektroKill pushed a commit to dnSpyEx/ILSpy that referenced this issue Aug 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C# Decompiler The decompiler engine itself Enhancement Areas for improvement
Projects
None yet
Development

No branches or pull requests

2 participants