docs/features/field-nullability.md
field keyword nullable analysis implementationSee also specification.
In order to decide the NullableAnnotation of a SynthesizedBackingFieldSymbol we need to:
There are some significant problems with directly exposing the nullable annotation on this field symbol.
It's tempting to avoid exposing the inferred nullable annotation in the public symbol model. This might be problematic for automated tooling which is trying to reason about nullable initialization. For example, if a diagnostic suppressor wants to suppress CS8618 nullable initialization warnings under certain conditions. How should it decide whether/why a property like string Prop => field ??= GetValue(); requires initialization in constructors? It's unclear to me whether this matters.
In the interests of simplicity (first make it correct, then make it fast), I'd like to move forward by not exposing the inferred annotation in the symbol model. Rather, we introduce a new internal API SynthesizedBackingFieldSymbol.GetInferredNullableAnnotation(), which NullableWalker will use to decide initial nullable state, report warnings, and so on. The implementation simply does an on-demand binding of the get accessor, then nullable analyzes and decides the nullable annotation. Care needs to be taken to avoid using this API in a re-entrant manner--so, the NullableWalker passes which are used to infer the nullability annotation must avoid calling it.
This means that the ordinary FieldSymbol.TypeWithAnnotations API would not expose the inferred nullability, and neither would IFieldSymbol.Type or IFieldSymbol.NullableAnnotation. Instead the field's nullability would always match the property's. This is something I would actually like to fix, maybe before merging to main. It feels like Quick Info, etc., should expose the inferred nullability through the ordinary APIs.
Once we get a handle on the behaviors of the getter null resilience, we can start talking about how to reduce cost associated with it. For example, we could try to reduce redundant binding, by forcing nullable annotations to be inferred prior to analyzing certain methods. Before processing a constructor in MethodCompiler, for example, maybe we would identify the getters that need to be compiled for purposes of inference, and force those to compile first.