| ▲ | Joker_vD 4 hours ago |
| Also, C23 (and C2y) is moving ever closer to actually having defined semantics. As in, "just because one branch of your code, for this particular input, has read a value out of an array's bounds, your whole program's execution doesn't suddenly rewinds back to the start (with I/O included) and NOPs/loops infinitely/executes random bytes after main()". Also, I'm personally still waiting for a "shift left" operator having cleaner wording in the standard: If E1 has an unsigned type, the value of the result is E1 × 2**E2, wrapped
around. If E1 has a signed type and nonnegative value, and E1 × 2**E2 is
representable in the result type, then that is the resulting value; otherwise,
the behavior is undefined.
What does "otherwise" means here? It can't mean "if E1 has an unsigned type, or negative value, or E1 × 2**E2 is unrepresentable, then the behavior is undefined" because the part "if E1 has an unsigned type" has already been covered by the previous clause. Does it mean "If E1 has negative value, or E1 × 2**E2 is unrepresentable, then the behavior is undefined"? Because that's a ridiculous ordinance: (-1) << 1 is UB?! |
|
| ▲ | jcranmer 3 hours ago | parent | next [-] |
| > Does it mean "If E1 has negative value, or E1 × 2*E2 is unrepresentable, then the behavior is undefined"? Because that's a ridiculous ordinance. That's exactly what it means, see the change mentioned in https://www.open-std.org/jtc1/sc22/wg14/www/docs/n723.htm. (Previously, in C89, left shift on signed integers was completely undefined, only unsigned integers had a defined left-shift operation.) My guess is that it has to do with signed-magnitude representation causing left shifts to be absolute garbage with negative numbers. You also have to remember that the standard having undefined behavior doesn't preclude implementations from defining that behavior, and this is especially true with the freewheeling nature of a lot of the original undefined behavior introduced in C89 and C99. > Also, C23 (and C2y) is moving ever closer to actually having defined semantics. Eh, having seen how some of the sausage gets made, there's definitely been some cases where the overeager urge to rip out undefined behavior from the standard has worsened the quality. |
| |
| ▲ | uecker 2 hours ago | parent | next [-] | | Well, you could start to help? (instead of just being there to make sure special interests are preserved in voting) I also do not see where the quality suffered. There were some rather minor issues we created and need to cleaned up. | | |
| ▲ | jcranmer 2 hours ago | parent [-] | | > (instead of just being there to make sure special interests are preserved in voting) This kind of attitude tends to make me suspect that you are disinclined to actually listen to my feedback. | | |
| ▲ | uecker 2 hours ago | parent [-] | | Prove it. Aaron told me explicitly that he was inviting people because he was not happy that there are too many with a user view point. And then a couple people show up that never contribute papers but consistently vote for some special interest. | | |
| ▲ | jcranmer an hour ago | parent | next [-] | | I joined WG14 and WG21 because I was worried that there were not enough people on either committees who had the perspective of someone who works in middle-end optimization rather than the frontend. I haven't written any papers for WG14 because so far all of the feedback I need to give can easily be given merely by discussion on the reflector or during the paper discussions (this is not the case for WG21, and I have consequently written papers for them), and--as a middle-end person--there is actually relatively little feature development for me to specifically propose. Most of the big features I'd want to see included already have active proposals (e.g., _Vec) and there's little reason for me to write a different proposal. Maybe you'll eventually see a paper from me if I take up some of the proposal work for CFP, or if I get around to writing a version of restrict that's based off of the Tree Borrows model. Or maybe if I turn some of my thoughts on multiple return values into a paper, though I don't actually have a proper frontend proposal for that that I like, so it's more likely to remain a reflector message. | |
| ▲ | uecker an hour ago | parent | prev [-] | | Just as a recent example. There is a case where the standard obviously is broken . With a lot of support by most of the committee and actual work by a couple of people there is a paper that aims to fix it. The only people who never constructively helped but still vote against this important repair is your block of other non-contributing people you are part of: https://github.com/c-lang-org/polls/blob/main/n3909.md (Also a counter proposal would be constructive.) | | |
| ▲ | uecker an hour ago | parent [-] | | But I apologize, I should not be so negative. But I hope you see where the frustration comes from. The cleanup efforts are driven by a lot of volunteer work. We could use any help and instead of people just voting against such works. |
|
|
|
| |
| ▲ | Joker_vD 2 hours ago | parent | prev [-] | | > My guess is that it has to do with signed-magnitude representation causing left shifts to be absolute garbage with negative numbers. First of all, why not make it implementation-defined? That seems rather more useful for the programmer. Second, I'm personally aware of only a single machine with signed-magnitude representation, IBM 7090, and its ALS (Accumulator Left Shift) instruction treated the sign bit as sticky; amusingly, LGL (Logical Shift Left) did as well, so you had to put the value being shifted into the MQ register, not the accumulator, when you wanted an unsigned shift. So... no, that can't be it? | | |
| ▲ | jcranmer 2 hours ago | parent [-] | | One of the tricky things is that undefined behavior is commonly understood as having an unlimited "blast radius" and implementation-defined behavior is commonly understood as not permitting that kind of "blast radius." So any extension point that needed that unlimited "blast radius" had to be undefined behavior. (This has changed somewhat in modern times, but it does hold a little bit today--I'd point you to the discussion of N3863 at the most recent meeting, but I don't have any minutes that I feel really captured that discussion well enough.) And one of those kinds of "blast radius" is traps, and I suspect the original motivation for making signed integer overflow undefined behavior was precisely to preserve the ability to trap on signed integer overflow, which couldn't be implementation-defined. But the records of the discussions leading up to C89 and C99 are just too fragmentary for me to be certain. | | |
| ▲ | userbinator an hour ago | parent | next [-] | | The problem with UB, as evidenced over the years, is that it causes compiler writers to drift much too far from reality, despite the standard itself suggesting "behaving [...] in a documented manner characteristic of the environment" as one of the (better) choices. | |
| ▲ | Joker_vD an hour ago | parent | prev [-] | | > And one of those kinds of "blast radius" is traps, Wait. An implementation-defined behaviour isn't allowed to be trap/termination of execution? Okay, make it unspecified then, with the options of trapping or calculating some unspecified (not indeterminate) integer value. > N3863 Hm? Because implementation-defined behavior relies on the definition of unspecified behavior, it
means that implementation-defined behavior requires two or more possibilities to be defined in
the standard. However, a brief look at its uses in the standard clearly shows that there isn’t
always a list of potential behaviors to pick from.
Eh, I'd argue that omission of such a list is merely an implicit infinite ("two or more", you know) list of possibilities, its construction being self-evident. N3783 5.2.1.2p1.1: Physical source file multibyte characters are mapped, in an implementation-
defined manner, to the source character set (introducing new-line characters for end-of-line
indicators) if necessary.
There is an infinite list of possible ways to map multibyte characters to the source character set — an implementation has to pick one and document it. N3783 5.2.1.3p1: A conforming implementation shall produce at least one diagnostic message
(identified in an implementation-defined manner)
There is an infinite list of possible diagnostic messages... N3783 5.2.2.2p1-2: In a freestanding environment (in which C program execution can take
place without any benefit of an operating system), the name and type of the function called at
program startup are implementation-defined.
There is an infinite list of possible C identifiers that an implementation may chose as the name of the function to call at program startup. | | |
| ▲ | jcranmer an hour ago | parent [-] | | N3863 is one of those papers where what the paper proposed and what was actually discussed in the meeting ended up being rather different things, and it's the meeting discussion that I'm pointing to, not the paper itself. > Wait. An implementation-defined behaviour isn't allowed to be trap/termination of execution? Okay, make it unspecified then, with the options of trapping or calculating some unspecified (not indeterminate) integer value. Unspecified behavior also isn't allowed to be a trap. The difference between unspecified and implementation-defined is whether or not the compiler has to document its choice; the difference between unspecified and undefined is whether or not the choice is limited. |
|
|
|
|
|
| ▲ | SAI_Peregrinus 3 hours ago | parent | prev [-] |
| I think it means what you think is ridiculous. Shifting the sign bit out is UB. Unrepresentable results are UB. |
| |
| ▲ | Joker_vD 3 hours ago | parent [-] | | > Shifting the sign bit out is UB Shifting into the sign bit is also UB. Can I please have a language that actually has bitwise operators with defined semantics? No CPUs had differentiated between SAL/SHL for more than 30 years, if that was the original justification. I'm fine with left shift trapping on overflow. I'm fine with it just wrapping around. I'm even fine with bitwise shifts simply unapplicable to ints ("cast to unsigned first, then cast back to int"). I am not fine with an operator that invokes UB for literally half of the values of the lhs's domains. | | |
| ▲ | uecker 2 hours ago | parent | next [-] | | I was thinking about eliminating this shift-into-left bit UB. There was a previous attempt though that failed to get consensus. (but long enough ago that one could try again). But all this signed overflow UB can be turned into traps using sanitizers.
In practice, generally mandating a trap is difficult though in ISO C. My plan is to mandate this as part of a special mode, in which case I would just leave this UB in the main standard. (One problem we also have that some special interests groups in WG14 are rather hostile to our work and the rest of the C community - so I encourage everybody who is actually using C and caring about it to join). | | |
| ▲ | Joker_vD an hour ago | parent [-] | | > I was thinking about eliminating this shift-into-left bit UB. At least make it "change of the sign bit is UB" — if 0 shifts into the sign-bit of a positive number, or 1 shifts into the sign-bit of a negative number, that shouldn't be UB. This would make left-shifts of negative numbers well-defined, their behaviour being that of two's complement numbers. And C23 already demands two's complement from the implementations, doesn't? > signed overflow UB can be turned into traps using sanitizers. Well, yeah, sure, but shifting -1 one place to the left doesn't overflow, it produces -2; only shifting INT_MIN or something up to and including INT_MIN + (INT_MAX/2) would overflow. |
| |
| ▲ | dcrazy 3 hours ago | parent | prev [-] | | Swift: https://developer.apple.com/documentation/swift/binaryintege... > The << operator performs a smart shift, which defines a result for a shift of any value. > - Using a negative value for rhs performs a right shift using abs(rhs). > - Using a value for rhs that is greater than or equal to the bit width of lhs is an overshift, resulting in zero. > - Using any other value for rhs performs a left shift on lhs by that amount. And while the docs don’t say it, `Int.max << 1 == -2`. And in fact, `T.max << 1 == -2` for all `T: BinaryInteger`. |
|
|