Roslyn: Suggestion: have the compiler optimise a multiple-assignment expression bodied constructor

Created on 1 Feb 2017  路  13Comments  路  Source: dotnet/roslyn

With reference to this Stack Overflow question, the OP was asking whether there was a way to replace the following with an expression-bodied constructor:
cs public Person(string name, int age) { Name = name; Age = age; }
The answer I supplied was that it could be replaced with:
cs public Person(string name, int age) => (Name, Age) = (name, age);

Whilst (IMO at least) a neat solution, it suffers from hiding some overhead. As I understand it, from reading the Deconstruction doc, the compiler will effectively convert that one line to something like:
cs public Person(string name, int age) { var temp = ValueTuple.Create(name, age); Name = temp.Item1; Age = temp.Item2; }

Assuming this is felt to be a useful pattern (and other folk may disagree with my view that it is!), then it would be good if the compiler could detect this pattern within a constructor and skip the tuple creation part. Instead it could convert it to the equivalent of the original piece of code:
cs public Person(string name, int age) { Name = name; Age = age; }

Is this desirable, and if so, is it achievable?

Area-Compilers Feature Request New Language Feature - Tuples Resolution-Fixed Tenet-Performance

Most helpful comment

@alrz

Why would possibly anyone want to provide a custom ValueTuple that is not identical to ValueTuple?

Been working on managed languages for a decade now. I know longer ask why customers do things, I just accept that they will and plan accordingly. 馃槃

All 13 comments

I agree, when we are immediately deconstructing a tuple literal, it shouldn't create an intermediate ValueTuple, however, it would require some special-casing regarding evaluation order, for example:
```
(x, y) = (y, x);

When any of expressions contain any symbol mentioned in preceding elements, it should create a temp,
```cs
var t = x;
x = y;
y = t;

PS: "Any symbol" might be too broad, perhaps it should only consider symbols that are being assigned.

One item to keep in mind here is that the definition of ValueTuple can be controlled by the user. Hence it's observable when we optimize away usages of it. There is a separate dicsussion about whether or not this is okay, but we have to understand it is observable.

Would it be possible to determine if it has been implemented by the user as an enabling factor for optimizations such as this?

This constructor currently compiles to:

public Person(string name, int age)
{
    ValueTuple<string, int> expr_0D = new ValueTuple<string, int>(name, age);
    string item = expr_0D.Item1;
    int item2 = expr_0D.Item2;
    this.<Name>k__BackingField = item;
    this.<Age>k__BackingField = item2;
}

Perhaps the solution is to punt on this and rely on #15929 for any sort of tuple eliding optimizations.

Why would possibly anyone want to provide a custom ValueTuple that is not identical to ValueTuple?

@alrz

Why would possibly anyone want to provide a custom ValueTuple that is not identical to ValueTuple?

Been working on managed languages for a decade now. I know longer ask why customers do things, I just accept that they will and plan accordingly. 馃槃

@jaredpar

RE "why customers do things" simple, they will do so, because you have let them. 馃槃 Seriously though, is that enough reason to scratch out this proposal? It could be at least against the spec or something.

@bbarry

Would it be possible to determine if it has been implemented by the user as an enabling factor for optimizations such as this?

One user is equal to another from the perspective of the compiler. It's not important where ValueTuple comes from, just that it exists somewhere.

@alrz

Seriously though, is that enough reason to scratch out this proposal?

No definitely not a reason to discard it. I tried to be careful to point out that we just need to be up front about the trade off here.

@jaredpar

Given that the compiler already applies special rules regarding deconstruction of tuples I don't think that it's too far-fetched for the compiler to also apply special rules to deconstructing a tuple literal where there is no need to actually rely on a ValueTuple instance. It's not like this theoretical custom ValueTuple can change how its deconstructed; it's still required to provide accessible fields following the expected naming convention.

The only things that are affected by this are generic constraints, the constructor and the type-level initializers which won't run in case of a custom ValueTuple. That could be considered a breaking change like #6867. But I suspect it's a rare situation.

Fixed by #17027
With the fix, (x, y) = (1, 2) doesn't need to invoke the ValueTuple constructor.

@jcouv,

Could you clarify please, will #17027 make it into C# 7 (for VS2017 RTM), or will this appear in C# 7.1+?

The plan of record is for this optimization to ship with 7.1 (along with PRs currently being merged with milestone=2.1).

Was this page helpful?
0 / 5 - 0 ratings