dotnet / csharplang

The official repo for the design of the C# programming language
11.51k stars 1.03k forks source link

`field` keyword nullability: short and long term solutions #8425

Open RikkiGibson opened 1 month ago

RikkiGibson commented 1 month ago

See also https://github.com/dotnet/csharplang/blob/main/proposals/field-keyword.md#nullability See also #8360

We've been exploring how to provide a great experience for nullable analysis of properties using field keyword. We have considered introducing interprocedural analysis through concepts like "getter null-resilience" or through full interprocedural analysis of the constructor and accessors.

We need more time in order to arrive at the right place with these ideas. However, there is not much time left to get changes into .NET 9. So, we need to start thinking about short term versus long term solutions to this problem.

Short-term solution

It was previously suggested that we could handle the short term by simply treating properties that use the field keyword the same way we treat auto-properties today. This means that "lazy" property cases will need to suppress constructor warnings. The constructor warning will serve as a sign that users writing lazy properties are wandering into "unchecked" territory. Specifically, the field's initial flow state in the accessors will be not-null, but the value will be null at runtime.

class C
{
    public string Prop
    {
        get => field ??= GetDefault();
        set => field = value;
    } // fix: assign 'null!' in property initializer

    public C() { } // warning: Prop should have non-null state when exiting constructor.
}

Alternative short-term solutions include:

Long-term solution

We want to ensure that the short-term solution will transition smoothly to the long-term solution.

Some of the long-term solutions we have considered include:

  1. analyze the get accessor to decide the nullable annotation of the field, or,
  2. analyze the setter and constructor in order to flow the nullable state of the field into the getter.

When considering the above scenario as written, similar observable behavior will occur with both long-term solutions.

Essentially, the biggest risk here is that early adopters may add = null!; initializers to their code, which they will forget to remove after upgrading to our long-term solution. Until that null! is removed, there is a possibility of the user introducing null-safety bugs into their code.

333fred commented 1 month ago

Discussed in LDM on the 18th: https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-09-18.md#nullability-in-field.