Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create bool value which is neither true or false

Tags:

c#

Disclaimer: It's not about Nullable<bool> or something.

I've seen someone created a bool value which was neither true or false. It was an example why exhaustive match is not possible in C#. Unfortunately, I've lost source code with an example, which I'd really like to restore.

So the question is: how to make default arm to be called in the following code?

bool b = GetMyWeirdBool()
switch (b)
{
   case true: Console.WriteLine("true"); break;
   case false: Console.WriteLine("false"); break;
   default: Console.WriteLine("neither o_0"); break;
}

IIRC there was some dirty hacking via Marshal or StructLayout or something.

like image 870
Alex Zhukovskiy Avatar asked Dec 11 '22 08:12

Alex Zhukovskiy


2 Answers

Probably a true with a value of 43 or similar. The point is: Boolean is a lie - it doesn't exist at the IL level. It is simply an integer with a value that is zero or non-zero. That works fine for the "brtrue" and "brfalse" opcodes for any boolean values (1,-1 or 42) - the problem comes when you do an equality test between two values that are meant to be booleans: a "ceq" opcode will fail for two true booleans that aren't actually equal. Likewise, a "switch" opcode (jump-table - commonly used for switch in C#) will fail if the values aren't what the compiler expected.

This may confuse some APIs, returning false on equality to another true value, but reporting true when compared via brtrue.

Via unsafe code treating an int as a bool:

static unsafe bool GetMyWeirdBool()
{
    int i = 42;
    return *(bool*)(&i);
}

via IL:

static bool GetMyWeirdBool()
{
    var method = new DynamicMethod("evil", typeof(bool), null);
    var il = method.GetILGenerator();
    il.Emit(OpCodes.Ldc_I4, 42);
    il.Emit(OpCodes.Ret);
    var func = (Func<bool>)method.CreateDelegate(typeof(Func<bool>));
    return func();
}

via a struct with overlapped fields:

[StructLayout(LayoutKind.Explicit)]
struct Evil
{
    public Evil(int value)
    {
        Boolean = false;
        Int32 = value;
    }
    [FieldOffset(0)]
    public bool Boolean;

    [FieldOffset(0)]
    public int Int32;

}
static bool GetMyWeirdBool()
{
    var val = new Evil(42);
    return val.Boolean;
}
like image 128
Marc Gravell Avatar answered Jan 27 '23 23:01

Marc Gravell


You could use a struct with explicit layout:

[StructLayout(LayoutKind.Explicit)]
public struct NotBool
{
    [FieldOffset(0)]
    public readonly bool Value;
    [FieldOffset(0)]
    private readonly int Int;

    public NotBool(int intValue)
    {
        Value = false;
        Int = intValue;
    }
}

Test:

NotBool notBool = new NotBool(255);
switch (notBool.Value)
{
    case true:
        break;
    case false:
        break;
    default:
        Debug.WriteLine("Default");
        break;
}
like image 27
Dmitry Avatar answered Jan 27 '23 21:01

Dmitry