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

Deconstruction assignment of expressions involving array access produces unexpected results #46864

Closed
siegfriedpammer opened this issue Aug 16, 2020 · 2 comments
Assignees
Labels
Area-Compilers Resolution-By Design The behavior reported in the issue matches the current design
Milestone

Comments

@siegfriedpammer
Copy link
Contributor

We stumbled across this one while implementing the deconstruction feature in ILSpy's decompiler engine.

Version Used:
Microsoft (R) Visual C# Compiler version 3.7.0-6.20375.2 (34202cc)

Steps to Reproduce:

  1. compile the following code using csc.exe:
using System;
public class C {
    public (int, int) GetTuple()
    {
        Console.WriteLine("GetTuple");
        return (0, 1);
    }
    
    public static void Main() => new C().M();
    
    public void M()
    {
		try {
			Console.WriteLine("before deconstruction");
			(GetArray<int>()[GetIndex()], _) = GetTuple();
			Console.WriteLine("after deconstruction");
		} catch (Exception ex) {
			Console.WriteLine(ex.GetType().FullName);
		}
		try {
			Console.WriteLine("before normal access");
			GetArray<int>()[GetIndex()] = GetTuple().Item1;
			Console.WriteLine("after normal access");
		} catch (Exception ex) {
			Console.WriteLine(ex.GetType().FullName);
		}
    }
    
    static T GetValue<T>()
    {
        Console.WriteLine("GetValue");
        return default(T);
    }

    static T[] GetArray<T>()
    {
        Console.WriteLine("GetArray");
        return null;
    }

    static int GetIndex()
    {
        Console.WriteLine("GetIndex");
        return 0;
    }

}
  1. run the compiled exe

Expected Behavior:

I would expect the evaluation order involving deconstruction to match the evaluation order of normal array access, that means, evaluate the expression from left to right:

GetArray<int>()[GetIndex()] = GetTuple().Item1;

executes as follows:

  1. GetArray
  2. GetIndex
  3. GetTuple

so it produces the following output:

before deconstruction
GetArray
GetIndex
GetTuple
System.NullReferenceException
before normal access
GetArray
GetIndex
GetTuple
System.NullReferenceException

Actual Behavior:

It produces the following output:

before deconstruction
GetArray
GetIndex
System.NullReferenceException
before normal access
GetArray
GetIndex
GetTuple
System.NullReferenceException

Notice that the evaluation of the GetTuple call never occurs in the deconstruction case, unlike the normal array access case.

Is this a bug or just an inconsistency in the deconstruction evaluation order compared to "standard" C# rules?

@gafter
Copy link
Member

gafter commented Aug 30, 2020

@jcouv I suspect that this order of evaluation is specified.

@jcouv jcouv added this to the 16.9 milestone Sep 1, 2020
@jcouv
Copy link
Member

jcouv commented Feb 6, 2021

Yes, the order of evaluation is specified and this order is expected.

In a deconstruction, we completely evaluate the left-hand side expressions first, as we do in a compound assignment. But in a simple assignment, the indexing is delayed a bit. I find the behavior of simple assignment a bit more surprising myself :-/

I'll go ahead and close the issue as by-design. Thanks

@jcouv jcouv closed this as completed Feb 6, 2021
@jcouv jcouv added the Resolution-By Design The behavior reported in the issue matches the current design label Feb 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers Resolution-By Design The behavior reported in the issue matches the current design
Projects
None yet
Development

No branches or pull requests

4 participants