3

I used to think that in 64-bit Obj-C runtime BOOL is actually _Bool and it's a real type so it's safe to write like this:

BOOL a = YES; 
BOOL b = NO;
if (a != b) {...}

It's been working seemingly fine but today I found a problem when I use bit field structs like this:

typedef struct
{
    BOOL flag1 : 1;
} FlagsType;

FlagsType f;
f.flag1 = YES;

BOOL b = YES;

if (f.flag1 != b)
{
    // DOES GET HERE!!!
}

It seems that BOOL returned from the bit field is equal to -1 while the regular BOOL is 1, and they are not equal!!!

Note that I am aware of the situation when an arbitrary integer number is cast to BOOL and therefore becomes a "strange" BOOL which is not safe to compare.

However in this situation, both flag1 field and b were declared as BOOL and never cast. What is the problem? Is this a compiler bug?

The bigger question is if it's really safe to compare BOOLs at all or should I write a XORing helper function? (It would be such a chore, because boolean comparisons are so ubiquitous...)

user1548418
  • 457
  • 4
  • 14
  • The bit field type should be `int` or `unsigned int`, not `BOOL`. – rmaddy Jun 30 '15 at 15:39
  • BTW - you're not really comparing two `BOOL` values in your second example. You are comparing a `BOOL` and a "not really a `BOOL`". – rmaddy Jun 30 '15 at 15:42
  • "The bit field type should be int or unsigned int, not BOOL" who said that? And why is it -1? – user1548418 Jun 30 '15 at 16:28
  • Just about every Google search result for "C bit fields". – rmaddy Jun 30 '15 at 16:30
  • I think I get your point. If bit fields are returned as integers and BOOL is defined as signed char than it can be only -1 (YES) and 0 (NO). Thanks! – user1548418 Jun 30 '15 at 16:47
  • Then the answer to the bigger question: is it safe to compare BOOLs is yes? (except bit fields and integers cast to BOOLs) – user1548418 Jun 30 '15 at 16:48
  • You should see http://stackoverflow.com/questions/11134352/in-objective-c-safe-and-good-way-to-compare-2-bool-values – rmaddy Jun 30 '15 at 17:22

1 Answers1

3

I do not repeat that using a C boolean type solves the problems one can have with BOOL. That's true – in particular here, as you can read below –, but most of the problems resulted from a wrong storage into a boolean (C) object. But in this case _Bool or unsigned (int) seem to be the only possible solution. (Except of solutions with extra code.) There is a reason for it:

I cannot find a precise documentation of the new behavior of BOOL in Objective-C, but the behavior you found is something between bad and buggy. I expected the latest behavior to be analogous to _Bool. That's not true in your case. (Thanks for finding that out!) Maybe this is for backwards compatibility. To tell the full story:

In C an object of the type int is signed int. (This is a difference to char. For this type the signedess is implementation defined.)

— int, signed, or signed int

ISO/IEC 9899:TC3, 6.7.2-2

Each of the comma-separated sets designates the same type, […]

ISO/IEC 9899:TC3, 6.7.2-5

But there is a weird exception for historical reasons:

If the int object is a bit-field, it is implementation defined, whether it is a signed int or an unsigned int. (Likely this is because some CPUs in the past could not automatically expand the sign of a partial byte integer. So having an unsigned integer is easier, because nulling the top bits is enough.)

On clang the default is signed int. So according to full-width integers int always denotes a signed integer, even it has only one bit. An int member : 1 can only store 0 and -1! (Therefore it is no solution to use int instead.)

Each of the comma-separated sets designates the same type, except that for bit-fields, it is implementation-defined whether the specifier int designates the same type as signed int or the same type as unsigned int.

ISO/IEC 9899:TC3, 6.7.2-5

The C standard says that a boolean bit-field is an integer type and therefore takes part on the weird integer signedness rule for bit-fields:

A bit-field is interpreted as a signed or unsigned integer type consisting of the specified number of bits.

ISO/IEC 9899:TC3, 6.7.2.1-9

This is the behavior you found. Because this is meaningless for 1 bit booleans types, the C standard explicitly denotes that storing a 1 into a boolean bit-field has to compare equal to 1 in every case:

If the value 0 or 1 is stored into a nonzero-width bit-field of type _Bool, the value of the bit-field shall compare equal to the value stored.

ISO/IEC 9899:TC3, 6.7.2.1-9

This leads to the strange situation, that an implementation can implement booleans of width 1 as { 0, -1 }, but has to fulfill 1 == -1. Great.

So, the short story: BOOL behaves like an integer bit-field (conforming to the standard), but does not take part on the extra requirement for _Bools.

I think this is, because of legacy code. (One could expect -1 in the past.)

Amin Negm-Awad
  • 16,582
  • 3
  • 35
  • 50