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

[Proposal Question]: Keywordness of field #6530

Closed
333fred opened this issue Oct 12, 2022 · 6 comments
Closed

[Proposal Question]: Keywordness of field #6530

333fred opened this issue Oct 12, 2022 · 6 comments
Labels
Proposal Question Question to be discussed in LDM related to a proposal

Comments

@333fred
Copy link
Member

333fred commented Oct 12, 2022

field issue: #140

The problem

The implementation for field has hit a number of circularity issues, the most recent of which caused the feature to miss C# 11 as we lacked the time to design around it for the release. Not to get too into details, the crux of the particular issue we hit:

unsafe struct S
{
    public object Prop
    {
        get
        {
            S s = new();
            var ptr = &s;
            return field;
        }
    }
}

The type of ptr here can change depending on whether or not S fields that are reference types. In order to determine whether s has fields that are a reference type, we need to bind Prop, because it has a reference to a field and we need to know whether that resolves to a field keyword or not. In order to bind the property, we need to know what the type of ptr is... and we get into a cycle.

This is not the first such problem that we've run into, and while so far we've been able to work around them, previous problems have simply been a matter of delaying diagnostics to a later stage of the compiler. This version of the problem actually affects method semantics, and isn't so easily delayed.

Proposed solutions

We have a few different proposed solutions, from breaking the world to various compromises between that and abandoning the feature

Make field always a keyword in property bodies

This is obviously the most breaking we could be here, forcing field to always bind to the implicit backing field, and requiring user code to change field to @field to continue working as it does today.

class C
{
     int field;
     int Prop => field; // C# 11: refers to C.field. C# 12: refers to C.k_<field>
}

A possible 1.b solution would be to choose a different keyword than field, one that we feel comfortable making this change on.

Ignore scopes when determining if field already has meaning

Today, we need to do a full semantic bind of the body because we need to determine whether field actually meant the backing field, or if it, for example, bound to a local variable named field. By design, the following scenario is ok:

class C
{
    int Prop
    {
        {
            int field = 1;
        }
        return field; // Binds to C.k_<field>
    }
}

We could instead achieve an up-front check that does not require binding the method body with a more restrictive set of rules:

  • The identifier field in a property binds to the backing field if both of the following are true:
    1. There is no accessible type or member in scope at the property named field
    2. There are no local, local function, parameter, or other declarations named field in the body of the property. This is a purely syntactic check, and scoping is ignored.

These rules mean the above scenario no longer compiles, and it resolves the circularity from The problem by allowing us to determine whether the type is managed without needing to bind the property, cutting off the recursion.

Add an introducer modifer

This is the async/await-style solution. We introduce a new modifier on a property that means the property has a backing field, and in these new properties field unambiguously always means the backing field. @field will be required to access things named field that are not the backing field. The keyword I've chosen for this strawman is deliberately bad so the reader can fill in what they would prefer.

class C
{
    int field = 1;
    hasbackingfield int Prop => field; // Binds to C.k_<field>
}

Tell the compiler team to figure it out

The final option I have to bring is the brute force one: tell the compiler team to figure it out. We have some ideas on how to work around this, but we're concerned both about the implementation and maintenance cost here. We don't think it's an insurmountable cost, but we do think it is a non-trivial scenario to work around, and the lack of simpler rules means that consumers will need to be able to reason about them as well.

@333fred 333fred added the Proposal Question Question to be discussed in LDM related to a proposal label Oct 12, 2022
@CyrusNajmabadi
Copy link
Member

Ignore scopes when determining if field already has meaning

This approach seems sane and desirable (ignoring that it is my suggestion). it disallows cases I think should not be allowed, and is easy-ish to implement.

@BreyerW
Copy link

BreyerW commented Oct 12, 2022

I also feel like ignoring scopes is reasonable enough unless final option turns out to be easier than expected but i doubt it

@333fred
Copy link
Member Author

333fred commented Oct 13, 2022

Discussed in LDM https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-10-12.md#keywordness-of-field. Conclusion: option 4, "Tell the compiler team to figure it out"
"

@alrz
Copy link
Member

alrz commented Oct 13, 2022

Make field always a keyword in property bodies

Wouldn't that be just another case for #4460 which is already championed?

@333fred
Copy link
Member Author

333fred commented Oct 13, 2022

Possibly, but we have yet to have the discussion on how far we'd go with #4460. field, in particular, would be extremely breaking a change. A search on github shows many thousands of type members named field.

@333fred 333fred closed this as completed Oct 13, 2022
@alrz
Copy link
Member

alrz commented Oct 13, 2022

4460 is already a big breaking change and I doubt if it would make an exception for field. This is actually a motivating issue to deal with that break sooner than later, and all in one step - if that is too much, I don't think 4460 is ever going to happen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Proposal Question Question to be discussed in LDM related to a proposal
Projects
None yet
Development

No branches or pull requests

4 participants