-
Notifications
You must be signed in to change notification settings - Fork 1.6k
<limits>: fix traps for integers
#5816
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
8fd1697 to
9833a4a
Compare
(cherry picked from commit c31c5e2)
Oh, however, this is arguably true for all non-promoted integer types. Due to usual arithmetic conversions, no arithmetic operation on |
Yes, @Alcaro mentioned that in Discord, and captured that in llvm/llvm-project#166053
You can try. |
|
Ah, I found that there's already LWG-554 which is closed as NAD. So perhaps we should make |
|
Thanks, I'll mention LWG-554 in the test.
I'm not sure. On one hand, the promotion happens. On the other, one can say that zero value does not change when it can be represented, whereas |
| static_assert(traps_<char> == traps_<int> && traps_<signed char> == traps_<int> && traps_<unsigned char> == traps_<int> | ||
| && traps_<short> == traps_<int> && traps_<unsigned short> == traps_<int> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@frederick-vs-ja suggests these should not trap either, according to LWG-554 resolution interpretation.
I'm not sure.
To me, those are strong arguments that the property is unimplementable and useless, and that the only correct answer is deprecation. edit: split off 'vacuous truth' clause from previous entry, added a note about is_modulo |
To me the intent is to reflect such a compiler option (if any), which doesn't seem wrong.
I think by that logic
To me that's implementable and useless, as |
Compiler flags that change numeric_limits sound scary to me. Especially with Modules enabled, since those can't #ifdef it. But it wouldn't be the first one, so I'll concede this point. (-fwrapv should set int's is_modulo to true, but neither libstdc++ nor libc++ do. I'll file bugs once the exact expected behavior of those traits is known, unless someone beats me to it.)
That logic depends on whether we read 'any' as 'at least one' or 'for every possible option'. If we can read such a simple word differently, using that word is a spec bug already; I agree with filing an LWG issue. Perhaps it should be simply changed to 'all'; vacuous all is true. (Even without vacuous statements, 'any' is wrong word - on a hypothetical processor where multiplication overflow returns zero, (int)65536 * (int)65536 will differ from the true value by an integer multiple of max() - min() + 1.) |
If |
ARM64 coverage being added in #5815 drawn my attention to that
numeric_limithastraps, and it is apparently defined incorrectly for some architectures.🪤 What are traps?
See https://en.cppreference.com/w/cpp/types/numeric_limits/traps.html
trapsshould betrueif there's an operation on a value of that type that traps.For integers, it is zero division, it usually traps, but still does not trap for certain compiler/platform combinations. Sometimes it also happens with signed overflow on division. See below.
Hypothetically, I imagine that other signed overflows, like incrementing
intcontainingINT_MAX, may trap, but I'm not aware if any hardware behaves like this, at least, not our target platforms.We all know that
bools are integers, but they are surprisingly exempt from zero division trap: when you divide byfalse, you actually divide by integer zero, so it is notboolvalue that traps 😕. Oh, andboolhas non-value representations, but they do not trap for us either.Floats normally don't trap. You need to both compile with
/fp:exceptand enable these exceptions to make them trap. The trait reflects the state after program starts, so enabling by setting control word does not count.Overall that part of the traits doesn't seem to be terribly useful feature. For platform-agnostic code, the information "it is trap" is not useful, it does not tell where is the trap and what is the trap. And for platform-specific code, you can know the behavior of exact platforms specifically without querying the trait.
➗ Integer division traps
On x64 and x86,, integer zero division triggers some interrupt, ultimately resulting in
STATUS_INTEGER_DIVIDE_BY_ZERO(0xC0000094) structured exception.On Arm64 zero integer division results in having zero at destination, and no trap.
However, MSVC inserts check for zero and
brkinstruction to get into the same trap 🐶.clang-cldoes not implement this behavior, and does not trap on Arm64.INT_MIN / -1division would also trap on x64 and x86. The exception code isSTATUS_INTEGER_OVERFLOW(0xC0000095).This integer division overflow does not trap on Arm64: MSVC does not insert any checks for that.
⚙️ Product code fix
We change the value of
trapsfor all integers exceptbools on all supported architectures.Except for Clang on Arm64, where we keep it
false.✔️ Coverage
The runtime test approach was rejected, as it needs UB to perform the actual behavior test.
So the test checks for the assumed behavior on current hardware and compiler.
It errors out on an unknown hardware.
This libc++ test still needs some fixes.
I have not checked ARM64 EC, but according to the 👮♂️ two policemen theorem 👮♀️, if it traps on x64, and traps on Arm64, then on Arm64 EC that is between both of them it also traps. And I expect from Clang to behave naturally and don't trap there.
🎃 Horror
This might be a mess when we link Clang and MSVC objects together, we define the same variable differently. On the other hand, we also mix behaviors in this case. The variable is
constexprand hence very inline, so the definition should not matter, it is expected to inline to query sites even in/Od.