Skip to content

Latest commit

 

History

History
78 lines (56 loc) · 5.75 KB

LDM-2024-03-04.md

File metadata and controls

78 lines (56 loc) · 5.75 KB

C# Language Design Meeting for March 4th, 2024

Agenda

Quote of the Day

  • "I think it's actually a good think to have that onericity. Onericity? I think that's a word I just made up."

Discussion

Breaking changes: making field and value contextual keywords

#7964
#7918

We started today by exploring a piece of community feedback on the previous proposal for introducing breaking changes: what if, instead of trying to make field behave like value does today, we try to make it a contextual keyword when in a property accessor? One thing that became clear here, after multiple discussion directions, was that we needed to determine what the most important bit to the LDM is here: is it most important to the LDM that field behaves identically to value (whether that's as a contextual keyword or an implicit parameter), or can we view them as unrelated concepts? We mostly think the former, but there are some members of the LDM that would prefer to have field have their desired semantics, even if that means it differs from value. There is a potential that, by changing value, we will break a concept that is understood by a large number of our users. We do hear more about people misunderstanding the meaning of value than not, but this could simply be a biased opinion because we only ever hear the complaints.

We then turned to talking about the advantages and disadvantages of using contextual keywords in these scenarios, and how broad we want to make the context. A number of LDM members were happy with the fairly constrained scope of the breaks and fixes here, and think that they end up being fairly straightforward. There are a few interesting wrinkles that need to be worked out:

  • What is the behavior of nameof(value)? Today, it's the string "value", value is actually a parameter named value. We didn't arrive at a concrete conclusion on this today.
  • How does this behave for indexers? value is a legal parameter for get-only indexers, does it need to remain so? And does it need to continue to be illegal for settable properties? We also did not arrive at a conclusion for this today.

After discussion, we are most in favor of field and value as contextual keywords. We then discussed the scope of this: does every usage of field and value within a property need to be escaped, just the simple names, or somewhere in between? The example we looked at for these is:

int Prop
{
    get
    {
        int field = 1; // If it's only simple names that need to be escaped, this is legal
        field = 2; // This needs to be escaped in every version; are we ok with the inconsistency between declaration and usage?
        this.field = 3; // This is unambiguous, but does it need to be escaped anyways?
    }
}

We ended up with 3 options:

  1. Only simple names need to be escaped. int field = 1 is legal, as is this.field, but field = 2 would need to be escaped to refer to the local or class field.
  2. Simple names and declarations need to be escaped. int field = 1; needs to be escaped, but this.field does not.
  3. All usages need to be escaped. Both int field = 1; and this.field need to be escaped.

Consistency is again a key point here. Existing contextual keywords, such as await within an async method body, must always be escaped, no matter how it's used. this.await, for example, must be written as this.@await. This ultimately convinced us that option 3 is right: if the goal here is to take breaks to make the language simpler, let's not introduce another set of rules around when a contextual keyword is legal to use unescaped.

Conclusion

We will move forward with the proposal to treat field and value as contextual keywords. We will treat them as contextual keywords in all usages within accessor bodies. More work will need to be done to determine the behavior of indexers, nameof(value), and whether value is a contextual keyword within a get body.

Overload resolution priority

#7706
https://github.com/dotnet/csharplang/pull/7906/commits/aa6ab11c7df001e807e956f5b056785588e8b12e

Finally today, we took a brief look at the proposal for overload resolution priority. We've previously looked at it in the form of BinaryCompatOnlyAttribute, but some particularly gnarly challenges with OHI convinced us that it was not the right approach. Instead, we went with a narrower approach of allowing an API author to adjust the relative priority of their methods to ensure that something that is better for a given domain, such as the Debug.Assert overload that takes a CallerArgumentExpressionAttribute, is preferred over what C# would normally choose. The LDM appreciated the specificity and narrow target of the new proposal, though there are likely some finer details to work out with where exactly the specification needs to be modified, and how it will interact with extension lookup in the new extension type world. We also did a quick dive on some of the open questions around inheritance with this new attribute: with the exception of a few things like parameter names and default values, the original definition of a virtual method is always the one used for determining applicability, and it seems likely that we'd want to do the same here, and not allow derived overrides to change the priority of a member; we did not concretely decide this, however, so we'll need to confirm when implementation starts.

Conclusion

Proposal is approved, and we'll work on it in the upcoming development cycle.