Skip to content

Latest commit

 

History

History
172 lines (132 loc) · 6.85 KB

L110-csharp-nullable-reference-types.md

File metadata and controls

172 lines (132 loc) · 6.85 KB

Handling of null reference types in C# code generation

  • Author(s): Tony Newell
  • Approver: apolcyn
  • Status: Draft
  • Implemented in: csharp
  • Last updated: 2023-10-26
  • Discussion at: (filled after thread exists)

Abstract

The protocol buffers compiler (protoc) and the gRPC C# compiler plugin (grpc_csharp_plugin) generate C# code that does not use the nullable reference types language features introduced in C# 8.0.

This document discusses the changes to be made to the code generation, the support in Grpc.Tools, and any impact on existing projects.

Background

Nullable reference types refers to a group of optional features introduced in C# 8.0 to enable the compiler to do static flow analysis to determine if a variable might be null before it is dereferenced. They are compile time features that do not affect the runtime.

Various issues have been raised asking for the code generated by the protocol buffers compiler (protoc), the gRPC C# compiler plugin (grpc_csharp_plugin), and Grpc.Tools to support nullable reference types, including:

Currently the code that is generated is compatible with C# 7.3 and is not in a nullable context since the // <auto-generated> comment in the code disables it.

No nullable checks are made and no compiler warnings are generated for this code, even when the code is compiled in a project with nullable checks enabled. This means that some nullable checks that could be useful in user code are not done.

Note: here we are not proposing any changes to the API or semantics to support protobuf optional fields as nullable, as sometimes requested (e.g. - C#: Consider exposing protobuf optional fields as nullable properties). That has already been addressed in the above issue.

Proposal

The proposal is to add optional support for generating code that is aware of the nullable reference types support in C#. Existing projects that do not want to use this feature (for example, those using C# 7.3) will continue to work with the code generated without nullable support. Projects using C# 8.0 can take advantage of the nullable support if they so wish.

Code generation

Options will be added to protoc and grpc_csharp_plugin to enable generating of code that supports nullable reference types. By default (without the new option) the code generated will be the same as today.

Proposed name of option: enable_nrt

e.g.

protoc --plugin=protoc-gen-grpc=grpc_csharp_plugin \
    --csharp_out=. \
    --csharp_opt=enable_nrt \
    --grpc_opt=enable_nrt \
    myproto.proto

The details of the changes to the generated code are described below.

MSBuild integration

Grpc.Tools will be changed to detect if a project has nullable enabled and automatically add the options to protoc and grpc_csharp_plugin if needed.

A mechanism will be added to override this default behaviour. This will allow users to explicitly choose whether the code generated has nullable support or not. For example, it may be that the user wants to generate code that is compatible with other projects using an older compiler. This override will be done by setting the MSBuild property gRPC_NullableReferenceTypes to either enable or disable.

Rationale

The Nullable reference types feature was introduced in C# 8.0 in 2019. Supporting this language feature will give gRPC users information about null references in their code. For example:

User's protocol buffers file:

message User {
  string name = 1;
  Address address = 2;
}

message Address {
  string street = 1;
  string city = 2;
}

User's C# code:

public override Task<Empty> AddUser(User user, ServerCallContext context)
{
    AddressEntity entity = new
    {
        Street = user.Address.Street, // Possible null reference exception
        City = user.Address.City,
    };
    _addressRepository.Add(entity);

    // rest of the method
}

Right now this will happily compile without warnings. If the generated code had added the annotations for nullable reference types then the user would get a warning and would know that User.Address could be null.

Implementation

There are three projects that need to be changed to fully support nullable reference types:

It will require collaboration between the Protocol Buffers team and the gRPC team to coordinate the release of this feature.

Below lists the known changes that will be needed. Other changes needed may become apparent during development.

Protocol buffers code generation

Add the enable_nrt option, and when enabled changes will include:

  • add #nullable directive
  • add annotations to Message fields as they can be null
  • Equals(obj) and Equals(T) would have nullable parameters
  • MergeFrom(T) would have a nullable parameter

gRPC plugin code generation

Add the enable_nrt option, and when enabled changes will include:

  • add #nullable directive
  • BindService would have a nullable parameter
  • add annotations to the headers parameter on gRPC client methods

Grpc.Tools changes

In Grpc.Tools the following needs to be done:

  • Conditionally set property gRPC_NullableReferenceTypes if not already set by checking the value of the Nullable property
  • Add option to _GrpcOutputOptions and _OutputOptions depending on the value of gRPC_NullableReferenceTypes

Existing PRs

There are already implementations of the above with varying degrees of completeness:

Related work

There is separate and ongoing work to implement nullable annotations in the Google.Protobuf runtime. This is not part of this proposal which is focused on the code generation.

See: