Roslyn: Inconsistent behavior for bitwise operations with Nullable<bool> typed local variable and Nullable<bool> typed member binding expression

Created on 24 Jan 2020  路  9Comments  路  Source: dotnet/roslyn

Here is some sample code. Checked it in console app targeting netcoreapp3.1.

public class SomeObject
{
    public bool BoolValue;
}

public class OtherClass
{
    public static void Process(SomeObject obj) // assume null passed
    {
        var value = obj?.BoolValue;
        var res1 = obj?.BoolValue | true; // = null
        var res2 = value | true; // = true

        var res3 = obj?.BoolValue & false; // = null
        var res4 = value & false; // = false
    }
}

Actual:
res1 != res2 and res3 != res4

Expected:
res1 == res2 and res3 == res4

Area-Compilers Bug

Most helpful comment

Both generate _interesting_ IL

All 9 comments

Tweaking things to:

public static void Process(SomeObject obj) // assume null passed
{
    var valz = obj?.BoolValue;
    var res1 = obj?.BoolValue | GetTrue();
    var res2 = valz | GetTrue();


    var res3 = obj?.BoolValue & GetFalse();
    var res4 = valz & GetFalse();

    Console.WriteLine($"res1: {res1}");
    Console.WriteLine($"res2: {res2}");
    Console.WriteLine($"res3: {res3}");
    Console.WriteLine($"res4: {res4}");
}

public static bool GetTrue()
{
    return true;
}

public static bool GetFalse()
{
    return false;
}

Spits out:

res1: True
res2: True
res3: False
res4: False

@aka-STInG and @Clockwork-Muse I think it has to do with the defined bool? bitwise operator truth table, it works differently for | and &. See below:

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nullable-value-types#operators

For bool? operands, the & and | operators support the three-valued logic. The semantics of these operators is defined by the following table:

x | y | x&y | x\|y
-- | -- | -- | --
true | true | true | true
true | false | false | true
true | null | null | true
false | true | false | true
false | false | false | false
false | null | false | null
null | true | null | true
null | false | false | null
null | null | null | null

@brianpursley I'm a bit confused. How this table explain difference between
null | true in one case is null and in other is true?
@Clockwork-Muse It seems like in your case method invocation instead of literal causes it to produce valid output. It would be nice to have valid output for all cases.
It looks like expression obj?.BoolValue | true is actually transformed to something like this obj != null ? obj.BoolValue | true : null.

Aren't you using | for res2 and & for res4?

OK maybe I misunderstood the issue. Let me check again

No you are right. This is seems wrong. It isn't even consistent with itself:

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            SomeObject obj = null;

            Console.WriteLine($"result1 = {obj?.BoolValue | true}");
            Console.WriteLine($"result2 = {true | obj?.BoolValue}");
        }

        private class SomeObject
        {
            public bool BoolValue;
        }

    }
}

Output:

result1 = 
result2 = True

EDIT: Well I guess this can be in part due to short circuiting. But I think you two are right, something strange is happening here.

Both generate _interesting_ IL

This is a language issue, not a codegen issue.

@jaredpar can you move this over to dotnet/roslyn?

Was this page helpful?
0 / 5 - 0 ratings