5 ms·
Unrelated, but yesterday I found out you can have two bools in C++ that are both true but do not equal each other, by reinterpret casting them from (u)ints. I t
by sk0g 4y ago
Unrelated, but yesterday I found out you can have two bools in C++ that are both true but do not equal each other, by reinterpret casting them from (u)ints. I think this was for the standard bool type too... Now I'm questioning the most basic of things.
- pjmlp 4y agoIt boils down to C compatiblity, every non zero numeric value is true, that language where good developers make no errors.
- sk0g 4y agoI know that's the case for ints, but in this case [0] the int was cast to a boolean, which I thought would ensure comparisons would perform as expected, but no such luck. [0] https://www.onlinegdb.com/tfHPZ_FOfY https://www.onlinegdb.com/tfHPZ_FOfY
- gpderetta 4y agoYou are not casting an int to a bool (which would indeed do the right thing) but casting a pointer to int to a pointer to bool which violates strict aliasing.
- sk0g 4y agoAh fair, thanks for pointing it out! Wasn't my issue, just the minimal reproduction of behaviour that happened over a few external modules, causing this issue.
- jstimpfle 4y agoThis is not strict aliasing as far as I understand. Strict aliasing is about inference of distinctness of pointers. The case here is that an invalidly typed pointer is created (a bool pointer pointing to where there is no bool). Not sure what this situation is called in standardese.
- gpderetta 4y agoThe type aliasing rule is also known as the strict aliasing rule, see for example: https://en.cppreference.com/w/c/language/object https://en.cppreference.com/w/c/language/object
- jstimpfle 4y agoAfter skimming this, I still think the strict aliasing rule is used by compiler to avoid re-reads. What you were talking about is probably something else, maybe an "invalid lvalue access" as per your link.
- gpderetta 4y agoFrom cpp reference: "Strict Aliasing: Given an object with effective type T1, using an lvalue expression (typically, dereferencing a pointer) of a different type T2 is undefined behavior, unless [non relevant exceptions omitted]". In this case the expression has type bool and the underlying object has type int, so it is a straightforward strict aliasing violation. With GCC you can compile with -fno-strict-aliasing to ignore this rule. But now you fall afoul of the rule that prevents accessing an invalid representation (i.e. a trap-representation) of an object. This rule is also described in the link I posted before, under the object representation paragraph.
- jstimpfle 4y agook, so in the case above, it's both (if strict aliasing is active). Makes sense now.
- bobbyi 4y agoThe strict aliasing violation is incidental. You can change int8_t to char in the code and get the same result with no strict aliasing violation.
- gpderetta 4y agoSure, as per my other comment, it then would be an object representation violation. Even ignoring both rules, there is still no reason to expect the assertion not to fire: int x = 2; assert(*(bool*)x == true); This is the same as: float x = 2.0 assert(*(int*)x == 2); Just because two object are convertible, there is no reason to expect their representation to be the same.
- jstimpfle 4y agoSometimes people indeed come up with stuff where I can't figure out why anyone would ever write that.
- sk0g 4y agoIt wasn't really one particular function, but an aggregation of behaviours over some external modules. None of it written by me, luckily :)
- MauranKilom 4y agoI'm very sure that's just UB. The standard requires unique representations of true and false (e.g. byte values of 1 and 0, but for all you care it could be 13 and 37). Converting an integer to bool (even implicitly, as in `if (3)`) is required to lead to those values in the abstract machine. If you somehow force a bool to be a different value (e.g. `*(int*)(&myBool) = 7`), that's UB.
- elteto 4y agoThis is undefined behavior. Compile with -fsanitize=undefined and run it, you will get a runtime error.
- sk0g 4y agoAnd so it does! Thank you, will see if I can keep it to prevent me from running into this issue. I have a feeling the Unreal codebase will be full of UB abuse though.
- elteto 4y agoUnfortunately most large C++ codebases are...
- Asooka 4y agoClang, MSVC and GCC all have options to turn off various flavours of UB, or rather to define the behaviour in those cases. I strongly suggest using -fwrapv -fno-strict-aliasing -fno-delete-null-pointer-checks (and the equivalent in MSVC) in every large project. That is the easiest UB to hit and while the program will have a bug, it will at least be easier to reason about and optimised vs debug builds will have the same behaviour. Debugging "the compiler deleted my if meant to catch and log an error condition because after the inlining pass some function dereferences a pointer, thus the pointer cannot be null, thus the if can be deleted" is... hard.
- pjmlp 4y agoAdditionally all of them have the ability to enable bounds checking and iterator validation in the standard library.