Herb Sutter's comment on why it's ok is confusing to me:
> Regarding the use of UB internally: It's okay and if anyone is worried about it the use of UB is benign on the platforms we target (e.g., they don't involve hitting any hardware trap representations for these types)
Isn't the outcome of the UB (ie. whether it will "rm -rf /" or something else) dependent on both the target and the compiler? And the compiler (or future compiler) could plausibly make the assumption that the narrowing to an unrepresentable value will never occur and change behaviour because of it?
wavemode 1 days ago [-]
For what it's worth, a GSL developer later reopened that GitHub issue and stated that they're going to look into fixing the UB. Sutter may have just been stating an assumption.
> I'll raise this issue in the next internal GSL sync. I'd agree with y'all that this behavior: https://godbolt.org/z/4Tr1fe9xG is undesirable
mort96 1 days ago [-]
But ... surely Sutter ought to know better than to say "because the hardware handles this conversion reasonably, it's a benign case of UB"? Surely he knows that compilers can and will optimize based on the assumption that UB never happens?
The problem isn't, "oh no what if my CPU's float->int conversion instruction traps", that's an extremely naive way to think about UB. Everyone who has thought seriously about UB in C++ for any length of time knows this. It's worrying that this was Sutter's response.
tialaramex 20 hours ago [-]
I think this actually demonstrates why Rust's safety culture is what's crucial, not the safety technology they have built to enable that culture and which is relatively easier to duplicate.
The natural instinct of humans is to deny problems. Their safety culture very strongly encourages Rustaceans encountering the equivalent issue [this really happened, you could write this nasty conversion bug in Rust 1.0 no problem but for years now Rust panics] to accept that there is a safety problem - and from there they can begin actually addressing the problem rather than pretending it doesn't exist. It's not perfect, but the alternatives are definitely worse.
The technology doesn't do this. The Rust compiler would be entirely OK with Rust shipping a standard library where safe APIs like Vec::pop can induce Undefined Behaviour. That's not allowed culturally, but technically Vec::pop already has an unsafe block, it could cause UB if it wanted to.
ameliaquining 18 hours ago [-]
Nitpick: Rust doesn't panic in this situation, it saturates (i.e., returns the largest possible value for the given integer type). Among other reasons, because a lot of existing programs that triggered this UB happened to work in practice, and would have experienced the panicking behavior as a severe regression.
This case also shows the limits of Rust's safety culture; they knew about the problem for a long time, and could have fixed it right away if they'd been willing to make programs that do a lot of float-to-int casts eat a performance regression, but a number of users objected strongly to this. So it remained unfixed until they figured out a way to make it fast enough that no one would really notice.
tialaramex 12 hours ago [-]
> Rust doesn't panic in this situation, it saturates
Yes sorry, in my head I'm thinking about what I'd want here because I do not like any of Rust's 'as' casts and in fact the thing I'd want here (TryInto) just does not exist on purpose for this reason. I wonder if I've ever run into this, realised I can't write a TryInto and if so what did I end up doing - interesting.
ameliaquining 5 hours ago [-]
It's actually quite straightforward to write a TryFrom impl that returns Err if the conversion fails (i.e., if the input floating-point value is infinite, NaN, or out of range of the target integer type). The reason this hasn't been done is, what if the input is within range but has a nonzero fractional part? Do you truncate or round (meaning the conversion can sometimes be lossy), or do you return Err unless the conversion can be done losslessly? It's not obvious which behavior is right, but they'd have to pick one.
tialaramex 2 hours ago [-]
Sure. These decisions are tricky, I had this concern in `realistic` too.
My `Rational` type is the "big rationals" (the finite subset of rational numbers I can represent with your available RAM) and these are certainly able to precisely represent any 32-bit or 64-bit float which isn't NaN or an infinity. However, vice versa is not true of course. 0.1 is a very easy Rational, but of course binary floating point cannot represent this exactly.
In the end I punted, TryInto<Rational> is implemented for f32 and f64 but the opposite is not provided at all.
dwattttt 18 hours ago [-]
It's interesting to consider that the same question would've surely come up during C and C++ language development, but that the answer is informed by the impact on the ecosystem, which was dramatically different when C, C++, and Rust were all being developed.
tialaramex 10 hours ago [-]
I don't think it would "surely come up". The C++ community is very comfortable with "Don't do that" as the lesson even though it's not actionable.
Like I said, safety is cultural. You can invent this stuff, somebody did, but the way most people end up doing it isn't because they all spontaneously invented the same solution, it was absorbed from their culture. C++ culture says "Don't do that" all the time instinctively. An actual concrete objection to fixing it might be offered if you insist on one, but they start with "Don't do that".
Sorting is my go-to example. Rust's sorts are safe. If I sort "Alligator", "Baboon", "Cat", "Donkey" then no matter what my ordering rule was nothing crazy happens. If my rule was nonsense, like "Every item is before every other item" then Rust might panic, it is allowed to do that - but otherwise it just will give me back the same four items but who knows what order because my ordering rule is nonsense. That seems easy enough, right?
In C++ if my ordering rule does not meet the precise requirements of the C++ language then all bets are off, that's Undefined Behaviour. I might get back "Cat", "Cat", "Cat", "Cat" even though there was originally only a single cat, or it might scribble past the end of my list of animals, crash, or anything at all. And while it would be permissible for real C++ standard library implementations to do better, on the whole they don't. "Don't do that" is the answer if you ask why this defect is allowed.
20k 1 days ago [-]
Yeah Herb's 100% wrong here. Its common when people are downplaying the memory safety issues with C++ that they say things like this, but its completely incorrect. All invoked UB is potentially equally serious, and this is exploitable memory unsafety. Compilers can and do optimise away this kind of stuff (as other people have explained here)
There's also important context in that Herb is currently one of the people leading the current memory safety approach for C++
jcranmer 1 days ago [-]
In LLVM, the result of floating-to-int conversion that is out of range of the int is a poison value, which means you get essentially the full unpredictability of UB.
That said, I'm a little hard-pressed to think of optimizations that would actually take advantage of poison, because floating-point range isn't really computed in the optimizer.
I don't know exactly which optimization passes do what, but a few observations:
* The 'foo(unsigned int n)' function should never return a value that's greater than 'n', since it returns 'i < n ? i : n'.
* The value printed by the 'foo' function should always be the same as the value that's returned.
Yet the value it prints is 2700624104 (which is greater than 'n', which is 10 in this case), and the returned value is 2700623376, which is different. (The exact numbers vary run to run)
If the conversion "just" resulted in a bogus value, we would have expected some number <=10 to be printed two times.
Maxatar 1 days ago [-]
Yes, this is all true but Sutter's comment is that the specific platforms that this specific implementation of the GSL targets results in the correct output. The platforms officially supported are:
GCC 12, 13, 14
XCode 14.3.1, 15.4
Clang 16, 17, 18
Visual Studio with MSVC VS2019, VS2022
Visual Studio with LLVM VS2019, VS2022
afdbcreid 24 hours ago [-]
Then this is, unfortunately, entirely wrong. Here's an example causing a segfault when there is a bound checks that the compiler omits:
At this point of time Herb Sutter was working for Microsoft. When he says "we" the compiler team is included.
What he means is that, it works for Microsoft as it is and zero fucks are given for other compilers and platforms.
pjmlp 1 days ago [-]
No, UB is allowed special powers for compiler and standard library implementors, which is what Herb Sutter means with internal behaviour.
Meaning MSVC is aware of these cases, so the compiler has special cases for it.
digitalPhonix 1 days ago [-]
That's my point - GSL is NOT MSVC only, it's a general purpose library and NOT a standard library implementation of a toolchain so any compiler is expected to be able to compile it (it also explicitly targets clang & gcc).
pjmlp 1 days ago [-]
It was originally created by Microsoft and as Herb mentions "all our target platforms", so most likely it gets special treatment.
aw1621107 1 days ago [-]
At the time Herb made that comment GSL was documented as supporting XCode 12.5.1/13.2.1, GCC 10/11, Clang 11/12, and Visual Studio 2019/2022 using both MSVC/LLVM [0]. Even if MSVC had special support for GSL I'm a bit more skeptical that such support would extend to XCode, GCC, and Clang.
GSL is not the standard library nor an internal runtime library. Its GitHub page claims that it supports a variety of compilers:
> The GSL officially supports recent major versions of Visual Studio with both MSVC and LLVM, GCC, Clang, and XCode with Apple-Clang
pjmlp 1 days ago [-]
It was originally created by Microsoft and as Herb mentions "all our target platforms", so most likely it gets special treatment.
Maxatar 1 days ago [-]
There is absolutely no special treatment afforded to the GSL by any of the target platforms. While we can't inspect MSVC's source code, both clang and GCC do not have any support or affordance for the GSL whatsoever and it would be very unusual to expect MSVC's source code to have some kind of affordance for this library.
achierius 1 days ago [-]
The 'special treatment' isn't technical, it's procedural -- insofar as if, during development for a new release, MSVC were to land some changes that broke GSL, Microsoft's testing would catch that and ensure that the changes were reverted or fixed to support the latter, prior to shipping. Since they're built as part of the same operating system, they can make sure not to step on one another's toes -- which is not a guarantee that they can make to third-party applications.
tsimionescu 1 days ago [-]
If you've ever read anything about the internal culture in Microsoft, you'd know this is extremely implausible.
Maxatar 1 days ago [-]
I have no idea where you possibly got this idea from since the Github Issues tracker for GSL has numerous instances of new releases of MSVC breaking GSL compilation.
ranger_danger 22 hours ago [-]
I think they're saying that since they denote specific compiler versions as "officially supported", they can get away with saying the "UB" is not an issue in those versions only because they've already tested its behavior there, and anything else you compile with is untested and unsupported 'here be dragons' land.
You may wish they target every possible compiler brand and version, but they are free to disagree with you and only "support" specific ones.
Unfortunately this also has the same effect as the Linux kernel now in that it is no longer technically compliant with any C++ (or C) standard.
Maxatar 21 hours ago [-]
How does a library writer testing that their library works with a specific version of a compiler that was already released have anything to do with a compiler implementation providing special treatment for that library?
Your interpretation contradicts the statement that "during development for a new release, MSVC were to land some changes that broke GSL, Microsoft's testing would catch that and ensure that the changes were reverted or fixed to support the latter, prior to shipping."
ranger_danger 21 hours ago [-]
> How does a library writer testing that their library works with a specific version of a compiler that was already released have anything to do with a compiler implementation providing special treatment for that library?
Not special treatment for the library, but the library being able to assume that specific UB handling in certain existing compiler versions exists, can be relied on, and is compatible with the library's assumptions of such behavior, as long as those are the only versions they officially "support."
Maxatar 21 hours ago [-]
Here is the original statement about special treatment:
"It was originally created by Microsoft and as Herb mentions "all our target platforms", so most likely it gets special treatment."
My claim is that Microsoft is not giving any special treatment to the GSL... the GSL is written using functionality that is officially available to any third party library, and hence not special.
Are you disputing this? If so then what is the special treatment you're claiming that Microsoft is giving to GSL or what special treatment is GSL making use of?
ranger_danger 21 hours ago [-]
I am not claiming there is any special treatment at all. I think the situation is much simpler:
They (Microsoft) test GSL with specific compiler versions to observe how their usage of the UB is treated, and if satisfactory, that compiler version is now "supported" in their eyes. They do not claim to "support" any other version because they have not tested them to know how the UB functions there.
Maxatar 19 hours ago [-]
What is the relationship between your claim, and the claim I replied to about GSL having special treatment? In other words, why did you reply to me with an interpretation about a post that claimed GSL is afforded special treatment by MSVC if your claim is that GSL does not get any special treatment from MSVC?
Did you intend to reply to someone else?
ranger_danger 4 hours ago [-]
I'm not sure exactly where the disagreement really is. Perhaps when OP said "ensure that the changes were reverted or fixed to support" you were thinking they meant MSVC reverting changes, while I was thinking of GSL doing it instead, in order to "support" specific compiler versions they have on hand at the time to test.
Either way I think we are on the same page in thinking that compilers don't know/care that GSL exists and aren't doing anything special for it, and that's good enough for me.
20k 1 days ago [-]
Clang is a target for the GSL though. How can MSVC's special powers prevent this from being exploitable UB in Clang/LLVM?
This code boils down to static_cast<int>(some_double); so nothing fancy is going on here
pjmlp 1 days ago [-]
Actually Microsoft has their own fork that ships with Visual Studio installer.
However that was me guessing from Herb Sutter's reply.
rhinocnc 21 hours ago [-]
yes, that is right
LoganDark 1 days ago [-]
UB is bad not because it actually leads to any particular result on any particular platform or compiler, but because semantically it invalidates assumptions about a program. Rust is explicit on this, but it absolutely still applies to C/C++.
em3rgent0rdr 1 days ago [-]
Well because it could lead to any result on some platform or compiler, it invalidates assumptions about the program.
LoganDark 1 days ago [-]
UB invalidates assumptions about the program not necessarily because it leads to arbitrary behavior in practice, but because it leads to arbitrary behavior in spirit. Even if there is no compiler in existence where the UB causes a problem, that does not make the program correct. UB is about whether the program is correctly defined, not about whether it works or not.
gpvos 1 days ago [-]
Sounds like the standard should say that it results in an implementation-defined value (or wording to that effect). Saying it's UB gives the compilers way too much leeway.
20k 1 days ago [-]
Its incredibly hard to get changes like this into the standard, because there's a core contingent of people who seem to feel that UB is part of C++'s identity, and then there's very vague hand waving about performance. There is luckily a pretty successful push in wg21 to start removing a lot of the more unnecessary UB, so hopefully this gets sent to the sausage factory as well
account42 11 hours ago [-]
You're the one doing the hand waving here. There are absolutely cases where UB is important for the optimizer. Float to int overflow is not one of them.
aw1621107 9 hours ago [-]
I think you might be misreading GP? They're basically agreeing with GGP ("so hopefully this gets sent to the sausage factory as well"); they're just saying that the process of actually changing the standard to remove (this kind of) UB is/has been rather challenging.
deepsun 23 hours ago [-]
Today people think that Java's main feature was OOP, but its main selling point was "no undefined behavior" (e.g. "int" means 32-bit signed integer with overflows, on any platform, no exceptions). Today it sounds normal, but back in the day that was what made Java popular.
pjmlp 12 hours ago [-]
As someone that jumped into Java already in 1996, even though it was still interpreted, JIT would only come into early 2000's, there was another big factor, the standard library.
Contrary to what people think nowadays, trying to write portable C or C++ code in the 90's was still an adventure.
C compilers were still getting C89 compliance, and POSIX wasn't as portable as folks think.
C++ was even worse, C++ARM was the C++ version of K&R C, compilers were more diverse than nowadays, each with their own frameworks, and C++98 was still a few years away.
Alongside Perl with CPAN, it was a big batteries box. Python wasn't that relevant yet.
pjmlp 1 days ago [-]
Hopefully this will be part of UB fixes for C++29, where plenty of UB is being redefined as erroneous behaviour instead.
It's sad that comparison operators in C/C++ can lead to UB. Comparing unsigned to signed ints or comparing floats to ints is something the compiler could make work reliably at very little extra cost.
codedokode 20 hours ago [-]
In C/C++ just adding two integers can lead to undefined behaviour. Your expectations are too high.
5 days ago [-]
lionkor 5 days ago [-]
The core guidelines library is definitely not doing the right thing here. Very odd.
dmitrygr 1 days ago [-]
> The correct fix is to bounds check before casting.
This will do wonders for speed. Actually explicitly using the safe isntr might be better. Something like this will happily compile to a single instr and cause you no grief even if the compiler had it out for you with UB. These instrs all clearly define outputs for all inputs (note that said outputs may not match across architectures)
static inline __attribute__((always_inline)) int f2i(float myFloat) {
int myInt;
#if defined(__arm__)
asm("VCVT.S32.F32 %0, %1":"=r"(myInt), "t"(myFloat));
#elif defined (__aarch64__)
asm("FCVTZS %0, %1":"=r"(myInt), "w"(myFloat));
#elif defined (__x86_64__)
asm("CVTTSS2SI %0, %1":"=r"(myInt), "x"(myFloat));
#else
#if 0 // be boring
if (myFloat <= TOO_SMALL_FLOAT || myFloat => TOO_BIG_FLOAT)
abort();
#else
#warning "Embrace the UB"
#endif
myInt = (int)myFloat;
#endif
return myInt;
}
orangepanda 1 days ago [-]
How could it be defined behaviour, when the result is different on ARM and x86?
cataphract 1 days ago [-]
Undefined behavior is not the same as implementation-defined or unspecified behavior. A program with undefined behavior is by definition an incorrect program. But there are cases where the spec actually gives some margin to the implementation. Programs relying on the choices of the implementation may be correct, even if non-portable.
Maxatar 1 days ago [-]
>A program with undefined behavior is by definition an incorrect program.
This is simply false and an oft repeated myth. Undefined behavior has a specific technical definition that is in the C++ standard [1] and there is absolutely no mention in that definition or the implication of that definition that undefined behavior necessarily results in an invalid or incorrect program.
The definition of undefined behavior, right from the standard itself is... and I quote... get ready for it...
"behavior for which this document imposes no requirements"
That's it, nothing more, nothing less.
The standard even goes out of its way to state the following:
"Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, *to behaving during translation or program execution in a documented manner* characteristic of the environment".
Behaving in a documented manner characteristic of an environment is a far cry from being by incorrect by definition.
>This document imposes no requirements on the behavior of programs that contain undefined behavior
That's saying that programs that exhibit undefined behaviour are not governed by the C++ spec. For a program to be a valid, spec governed piece of C++ code it has to exhibit no undefined behaviour (outside of some constraints). Its accurate to say that any undefined behaviour results in the code being executed no longer being C++, and it can have any behaviour. That's synonymous in common developer speak with 'incorrect', as its desirable for your C++ code to be executed as C++
Joker_vD 1 days ago [-]
Do I love selective quoting! "Undefined behavior may be expected... when a program uses an incorrect construct or invalid data". There is also "erroneous behaviour" which "is always the consequence of incorrect program code".
Honestly, you'd have a better argument by quoting that "Correct execution" can include undefined behavior and erroneous behavior, depending on the data being processed". Which is quite a wild sentence to read, but here we are.
wat10000 23 hours ago [-]
If the C++ standard imposes no requirements on such a program, then that means that the program is not C++. It's quite reasonable to describe such a program as an "incorrect [C++] program."
JdeBP 16 hours ago [-]
This is a semantic quagmire, and the threads on what undefined behaviour is are interminable. But just note this time around that correctness is not the same as conformity.
pjmlp 12 hours ago [-]
Semantics matter to compiler vendors though, which is why C++ 26 now introduced erroneous behaviour as an attempt to fix UB.
wat10000 7 hours ago [-]
What's the difference? There's no such thing as "incorrect C++, but still C++." A program is either correct/valid/conforming C++, or it's not. We can reasonably describe "not" as "incorrect," or "invalid," "nonconforming," or just "not C++."
cataphract 1 days ago [-]
I appreciate the correction. Although it should be said that modern implementations tend to opt to assume that UB never happens.
marcosdumay 1 days ago [-]
Architecture dependent is not the same as undefined.
stouset 1 days ago [-]
Also, the spec says it’s undefined. But compiler authors can always special-case their own compilers.
20k 1 days ago [-]
Clang/LLVM at least treats this as UB, not as implementation defined behaviour however according to an LLVM dev
Dwedit 24 hours ago [-]
Great, now converting a float to int will cause the C compiler to randomly reformat your hard drive...
> Regarding the use of UB internally: It's okay and if anyone is worried about it the use of UB is benign on the platforms we target (e.g., they don't involve hitting any hardware trap representations for these types)
Isn't the outcome of the UB (ie. whether it will "rm -rf /" or something else) dependent on both the target and the compiler? And the compiler (or future compiler) could plausibly make the assumption that the narrowing to an unrepresentable value will never occur and change behaviour because of it?
https://github.com/microsoft/GSL/issues/786#issuecomment-513...
> I'll raise this issue in the next internal GSL sync. I'd agree with y'all that this behavior: https://godbolt.org/z/4Tr1fe9xG is undesirable
The problem isn't, "oh no what if my CPU's float->int conversion instruction traps", that's an extremely naive way to think about UB. Everyone who has thought seriously about UB in C++ for any length of time knows this. It's worrying that this was Sutter's response.
The natural instinct of humans is to deny problems. Their safety culture very strongly encourages Rustaceans encountering the equivalent issue [this really happened, you could write this nasty conversion bug in Rust 1.0 no problem but for years now Rust panics] to accept that there is a safety problem - and from there they can begin actually addressing the problem rather than pretending it doesn't exist. It's not perfect, but the alternatives are definitely worse.
The technology doesn't do this. The Rust compiler would be entirely OK with Rust shipping a standard library where safe APIs like Vec::pop can induce Undefined Behaviour. That's not allowed culturally, but technically Vec::pop already has an unsafe block, it could cause UB if it wanted to.
This case also shows the limits of Rust's safety culture; they knew about the problem for a long time, and could have fixed it right away if they'd been willing to make programs that do a lot of float-to-int casts eat a performance regression, but a number of users objected strongly to this. So it remained unfixed until they figured out a way to make it fast enough that no one would really notice.
Yes sorry, in my head I'm thinking about what I'd want here because I do not like any of Rust's 'as' casts and in fact the thing I'd want here (TryInto) just does not exist on purpose for this reason. I wonder if I've ever run into this, realised I can't write a TryInto and if so what did I end up doing - interesting.
My `Rational` type is the "big rationals" (the finite subset of rational numbers I can represent with your available RAM) and these are certainly able to precisely represent any 32-bit or 64-bit float which isn't NaN or an infinity. However, vice versa is not true of course. 0.1 is a very easy Rational, but of course binary floating point cannot represent this exactly.
In the end I punted, TryInto<Rational> is implemented for f32 and f64 but the opposite is not provided at all.
Like I said, safety is cultural. You can invent this stuff, somebody did, but the way most people end up doing it isn't because they all spontaneously invented the same solution, it was absorbed from their culture. C++ culture says "Don't do that" all the time instinctively. An actual concrete objection to fixing it might be offered if you insist on one, but they start with "Don't do that".
Sorting is my go-to example. Rust's sorts are safe. If I sort "Alligator", "Baboon", "Cat", "Donkey" then no matter what my ordering rule was nothing crazy happens. If my rule was nonsense, like "Every item is before every other item" then Rust might panic, it is allowed to do that - but otherwise it just will give me back the same four items but who knows what order because my ordering rule is nonsense. That seems easy enough, right?
In C++ if my ordering rule does not meet the precise requirements of the C++ language then all bets are off, that's Undefined Behaviour. I might get back "Cat", "Cat", "Cat", "Cat" even though there was originally only a single cat, or it might scribble past the end of my list of animals, crash, or anything at all. And while it would be permissible for real C++ standard library implementations to do better, on the whole they don't. "Don't do that" is the answer if you ask why this defect is allowed.
There's also important context in that Herb is currently one of the people leading the current memory safety approach for C++
That said, I'm a little hard-pressed to think of optimizations that would actually take advantage of poison, because floating-point range isn't really computed in the optimizer.
I don't know exactly which optimization passes do what, but a few observations:
* The 'foo(unsigned int n)' function should never return a value that's greater than 'n', since it returns 'i < n ? i : n'.
* The value printed by the 'foo' function should always be the same as the value that's returned.
Yet the value it prints is 2700624104 (which is greater than 'n', which is 10 in this case), and the returned value is 2700623376, which is different. (The exact numbers vary run to run)
If the conversion "just" resulted in a bogus value, we would have expected some number <=10 to be printed two times.
GCC 12, 13, 14
XCode 14.3.1, 15.4
Clang 16, 17, 18
Visual Studio with MSVC VS2019, VS2022
Visual Studio with LLVM VS2019, VS2022
https://godbolt.org/z/8f6rv4dja
The example is adapted from a Rust example shown by @RalfJung in https://lobste.rs/s/ba2yfy/c_float_int_conversion_can_be_und....
What he means is that, it works for Microsoft as it is and zero fucks are given for other compilers and platforms.
Meaning MSVC is aware of these cases, so the compiler has special cases for it.
[0]: https://github.com/microsoft/GSL/tree/99a29ce797c8337b8923f2...
> The GSL officially supports recent major versions of Visual Studio with both MSVC and LLVM, GCC, Clang, and XCode with Apple-Clang
You may wish they target every possible compiler brand and version, but they are free to disagree with you and only "support" specific ones.
Unfortunately this also has the same effect as the Linux kernel now in that it is no longer technically compliant with any C++ (or C) standard.
Your interpretation contradicts the statement that "during development for a new release, MSVC were to land some changes that broke GSL, Microsoft's testing would catch that and ensure that the changes were reverted or fixed to support the latter, prior to shipping."
Not special treatment for the library, but the library being able to assume that specific UB handling in certain existing compiler versions exists, can be relied on, and is compatible with the library's assumptions of such behavior, as long as those are the only versions they officially "support."
"It was originally created by Microsoft and as Herb mentions "all our target platforms", so most likely it gets special treatment."
My claim is that Microsoft is not giving any special treatment to the GSL... the GSL is written using functionality that is officially available to any third party library, and hence not special.
Are you disputing this? If so then what is the special treatment you're claiming that Microsoft is giving to GSL or what special treatment is GSL making use of?
They (Microsoft) test GSL with specific compiler versions to observe how their usage of the UB is treated, and if satisfactory, that compiler version is now "supported" in their eyes. They do not claim to "support" any other version because they have not tested them to know how the UB functions there.
Did you intend to reply to someone else?
Either way I think we are on the same page in thinking that compilers don't know/care that GSL exists and aren't doing anything special for it, and that's good enough for me.
This code boils down to static_cast<int>(some_double); so nothing fancy is going on here
However that was me guessing from Herb Sutter's reply.
Contrary to what people think nowadays, trying to write portable C or C++ code in the 90's was still an adventure.
C compilers were still getting C89 compliance, and POSIX wasn't as portable as folks think.
C++ was even worse, C++ARM was the C++ version of K&R C, compilers were more diverse than nowadays, each with their own frameworks, and C++98 was still a few years away.
Alongside Perl with CPAN, it was a big batteries box. Python wasn't that relevant yet.
It's sad that comparison operators in C/C++ can lead to UB. Comparing unsigned to signed ints or comparing floats to ints is something the compiler could make work reliably at very little extra cost.
This will do wonders for speed. Actually explicitly using the safe isntr might be better. Something like this will happily compile to a single instr and cause you no grief even if the compiler had it out for you with UB. These instrs all clearly define outputs for all inputs (note that said outputs may not match across architectures)
This is simply false and an oft repeated myth. Undefined behavior has a specific technical definition that is in the C++ standard [1] and there is absolutely no mention in that definition or the implication of that definition that undefined behavior necessarily results in an invalid or incorrect program.
The definition of undefined behavior, right from the standard itself is... and I quote... get ready for it...
"behavior for which this document imposes no requirements"
That's it, nothing more, nothing less.
The standard even goes out of its way to state the following:
"Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, *to behaving during translation or program execution in a documented manner* characteristic of the environment".
Behaving in a documented manner characteristic of an environment is a far cry from being by incorrect by definition.
[1] https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/n49...
That's saying that programs that exhibit undefined behaviour are not governed by the C++ spec. For a program to be a valid, spec governed piece of C++ code it has to exhibit no undefined behaviour (outside of some constraints). Its accurate to say that any undefined behaviour results in the code being executed no longer being C++, and it can have any behaviour. That's synonymous in common developer speak with 'incorrect', as its desirable for your C++ code to be executed as C++
Honestly, you'd have a better argument by quoting that "Correct execution" can include undefined behavior and erroneous behavior, depending on the data being processed". Which is quite a wild sentence to read, but here we are.
https://bugs.llvm.org/show_bug.cgi?id=49599