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
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:
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?
Most helpful comment
Both generate _interesting_ IL