dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
19.02k stars 4.03k forks source link

MemberNotNullWhen is ignored on implicit boolean operators #55437

Open andrewmd5 opened 3 years ago

andrewmd5 commented 3 years ago

Version Used: Microsoft (R) Visual C# Compiler version 4.0.0-2.21362.3 (5f7ddf6f) (language version 9)

Steps to Reproduce:

Compile the following code:

 public readonly struct Result<TResult, TError> {

        public readonly TResult? Value;
        public readonly TError? Error;

        private readonly bool _isSuccess;
        private readonly bool _isError;

        [MemberNotNullWhen(true, nameof(Value))]
        public bool IsSuccess => _isSuccess;

        [MemberNotNullWhen(true, nameof(Error))]
        public bool IsError => _isError;

        private Result(TResult? result) {
            if (result is null) {
                throw new ArgumentNullException(nameof(result));
            }
            _isSuccess = true;
            Value = result;
            Error = default;
            _isError = false;
        }

        private Result(TError? error) {
            if (error is null) {
                throw new ArgumentNullException(nameof(error));
            }
            Error = error;
            _isError = true;
            _isSuccess = false;
            Value = default;
        }

        public void Deconstruct(out TResult? result, out TError? error) {
            result = Value;
            error = Error;
        }

        [MemberNotNullWhen(true, nameof(Value))]
        [MemberNotNullWhen(false, nameof(Error))]
        public static implicit operator bool(Result<TResult, TError> result) => result.IsSuccess;
}

Expected Behavior:

In the following codeblock datagram and datagramResult.Value should be known to roslyn not to be null :

Result<RelayDatagram, string> datagramResult = message.GetDatagram();
if (datagramResult) {
    var datagram = datagramResult.Value;
    var socket = GetSocket(datagram.DestinationHostname);
    if (socket is not null) {
        await socket.SendAsync((byte[])message.Message);
    }
}

Actual Behavior: The compiler reports CS8602 when accessing members of datagram even though datagramResult is evaluated to true

RikkiGibson commented 3 years ago

Related to https://github.com/dotnet/roslyn/issues/32671#issuecomment-693788347

We are getting some feedback that people need this, so maybe we can squeeze it into .NET 6.