Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
aka-STInG opened this issue Jan 24, 2020 · 9 comments
Assignees
Milestone

Comments

@aka-STInG
Copy link

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

@danmoseley danmoseley transferred this issue from dotnet/corefx Jan 24, 2020
@Clockwork-Muse
Copy link

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

@brianpursley
Copy link

@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

@aka-STInG
Copy link
Author

aka-STInG commented Jan 24, 2020

@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.

@brianpursley
Copy link

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

@brianpursley
Copy link

OK maybe I misunderstood the issue. Let me check again

@brianpursley
Copy link

brianpursley commented Jan 24, 2020

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.

@omariom
Copy link

omariom commented Jan 24, 2020

Both generate interesting IL

@AndyAyersMS
Copy link
Member

This is a language issue, not a codegen issue.

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

@stephentoub stephentoub transferred this issue from dotnet/runtime Feb 18, 2020
@gafter gafter added this to the 16.6 milestone Feb 18, 2020
gafter pushed a commit to gafter/roslyn that referenced this issue Apr 6, 2020
gafter pushed a commit to gafter/roslyn that referenced this issue Apr 6, 2020
gafter pushed a commit that referenced this issue Apr 7, 2020
@gafter
Copy link
Member

gafter commented Apr 7, 2020

Fixed in #43122

@gafter gafter closed this as completed Apr 7, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants