This is an archive of the discontinued LLVM Phabricator instance.

[ObjC] Add a -Wtautological-compare warning for BOOL
ClosedPublic

Authored by erik.pilkington on Jun 26 2019, 5:28 PM.

Details

Summary

On macOS, BOOL is a typedef for signed char, but it should never hold a value that isn't 1 or 0. Any code that expects a different value in their BOOL should be fixed.

rdar://problem/51954400 ObjC: Add diagnostics to catch incorrect usage of BOOL

Diff Detail

Repository
rL LLVM

Event Timeline

Herald added a project: Restricted Project. · View Herald TranscriptJun 26 2019, 5:28 PM

This only applies to relational operators, right? I'm a little uncomfortable with calling this "tautological" since it's not like it's *undefined behavior* to have (BOOL) 2, it's just *unwise*. But as long as we aren't warning about reasonable idioms that are intended to handle unfortunate situations — like other code that might have left a non-{0,1} value in the BOOL — I think this is fine.

This only applies to relational operators, right? I'm a little uncomfortable with calling this "tautological" since it's not like it's *undefined behavior* to have (BOOL) 2, it's just *unwise*. But as long as we aren't warning about reasonable idioms that are intended to handle unfortunate situations — like other code that might have left a non-{0,1} value in the BOOL — I think this is fine.

I think the party line is that it is undefined behaviour (in some sense), since UBSan will happily crash if you try to load a non-boolean value from a BOOL. It is a bit unfortunate that "defensive" code will start warning here though :/. Maybe we can try to detect and permit something like B < NO || B > YES, or emit a note with some canonical way of checking for non-boolean BOOLs. Even if we end up having to disable it default, I think its still a good diagnostic to have. A warning on stores to BOOL would probably be a lot higher value, though.

This only applies to relational operators, right? I'm a little uncomfortable with calling this "tautological" since it's not like it's *undefined behavior* to have (BOOL) 2, it's just *unwise*. But as long as we aren't warning about reasonable idioms that are intended to handle unfortunate situations — like other code that might have left a non-{0,1} value in the BOOL — I think this is fine.

I think the party line is that it is undefined behaviour (in some sense), since UBSan will happily crash if you try to load a non-boolean value from a BOOL.

What? Since when?

It is a bit unfortunate that "defensive" code will start warning here though :/. Maybe we can try to detect and permit something like B < NO || B > YES, or emit a note with some canonical way of checking for non-boolean BOOLs. Even if we end up having to disable it default, I think its still a good diagnostic to have. A warning on stores to BOOL would probably be a lot higher value, though.

I'm not sure this is a problem because I'm not sure there's any reason to write defensive code besides B != NO or B == NO. It's potentially problematic if someone writes B == YES, though.

This only applies to relational operators, right? I'm a little uncomfortable with calling this "tautological" since it's not like it's *undefined behavior* to have (BOOL) 2, it's just *unwise*. But as long as we aren't warning about reasonable idioms that are intended to handle unfortunate situations — like other code that might have left a non-{0,1} value in the BOOL — I think this is fine.

I think the party line is that it is undefined behaviour (in some sense), since UBSan will happily crash if you try to load a non-boolean value from a BOOL.

What? Since when?

https://reviews.llvm.org/D27607

It is a bit unfortunate that "defensive" code will start warning here though :/. Maybe we can try to detect and permit something like B < NO || B > YES, or emit a note with some canonical way of checking for non-boolean BOOLs. Even if we end up having to disable it default, I think its still a good diagnostic to have. A warning on stores to BOOL would probably be a lot higher value, though.

I'm not sure this is a problem because I'm not sure there's any reason to write defensive code besides B != NO or B == NO. It's potentially problematic if someone writes B == YES, though.

I was thinking about something like the following, which probably isn't worth warning on. Another way of handling it would be only emitting the diagnostic if !InRange. Not exactly sure how common that pattern actually is though.

void myAPI(BOOL DoAThing) {
  if (DoAThing > YES || DoAThing < NO) DoAThing = YES;
  // ...
}

This only applies to relational operators, right? I'm a little uncomfortable with calling this "tautological" since it's not like it's *undefined behavior* to have (BOOL) 2, it's just *unwise*. But as long as we aren't warning about reasonable idioms that are intended to handle unfortunate situations — like other code that might have left a non-{0,1} value in the BOOL — I think this is fine.

I think the party line is that it is undefined behaviour (in some sense), since UBSan will happily crash if you try to load a non-boolean value from a BOOL.

What? Since when?

https://reviews.llvm.org/D27607

Interesting; I'm not sure I find that convincing. Probably the least convincing part is where it links to its own behavioral documentation as justification for doing what it does. But okay, I guess we do this.

It is a bit unfortunate that "defensive" code will start warning here though :/. Maybe we can try to detect and permit something like B < NO || B > YES, or emit a note with some canonical way of checking for non-boolean BOOLs. Even if we end up having to disable it default, I think its still a good diagnostic to have. A warning on stores to BOOL would probably be a lot higher value, though.

I'm not sure this is a problem because I'm not sure there's any reason to write defensive code besides B != NO or B == NO. It's potentially problematic if someone writes B == YES, though.

I was thinking about something like the following, which probably isn't worth warning on. Another way of handling it would be only emitting the diagnostic if !InRange. Not exactly sure how common that pattern actually is though.

void myAPI(BOOL DoAThing) {
  if (DoAThing > YES || DoAThing < NO) DoAThing = YES;
  // ...
}

I don't think it's a problem to warn about this; this is just a silly way of writing if (DoAThing != NO) { DoAThing = YES; }.

rjmccall accepted this revision.Jul 8 2019, 1:32 PM

Okay.

This revision is now accepted and ready to land.Jul 8 2019, 1:32 PM

This only applies to relational operators, right? I'm a little uncomfortable with calling this "tautological" since it's not like it's *undefined behavior* to have (BOOL) 2, it's just *unwise*. But as long as we aren't warning about reasonable idioms that are intended to handle unfortunate situations — like other code that might have left a non-{0,1} value in the BOOL — I think this is fine.

I think the party line is that it is undefined behaviour (in some sense), since UBSan will happily crash if you try to load a non-boolean value from a BOOL.

What? Since when?

https://reviews.llvm.org/D27607

Interesting; I'm not sure I find that convincing. Probably the least convincing part is where it links to its own behavioral documentation as justification for doing what it does. But okay, I guess we do this.

Hmm, I guess you could say that they were being a little... tautological.

This revision was automatically updated to reflect the committed changes.
Herald added a project: Restricted Project. · View Herald TranscriptJul 8 2019, 4:46 PM