Date: Sun, 27 Aug 2023 11:38:27 -0400
On Sun, Aug 27, 2023 at 9:31 AM Frederick Virchanza Gotham via
Std-Proposals <std-proposals_at_[hidden]> wrote:
>
> I reply in series to Sebastian, Jason and Thiago below.
>
>
> Sebastian wrote:
> >
> > Not all instances of UB can be detected and not all instances of UB can be defined.
>
>
> I'm talk specifically about situations where the compiler makes the
> determination:
> "That would result in UB and I can therefore take certain
> liberties here, such as not bothering to check if the signed integer
> goes from positive to negative"
>
> So I'm talking specifically about situations where UB is detected.
But that's not really what's going on.
What's going on is that the compiler looks at your code and sees that
the only thing that changes `i` is the increment expression. And this
expression only ever increments the value. Incrementing a signed
integer cannot make it smaller. Therefore, the value only increases
from its original value. And therefore, the value can never become
less than its original value. The condition is thus trivially true.
No UB is being "detected". The compiler is simply not considering
behavior that the standard does not define as possible. Which is how
compilers *should work*. They should not consider things that cannot
be.
Lastly, you keep talking about "compiler" behavior. But you don't seem
aware that the standard cannot say the things you're trying to make it
say. It cannot coerce compiler behavior because the "behavior" in
question is *explicitly* outside of the boundaries that the standard
itself defines for C++.
This is a core problem of your idea: you're asking for a change from
the wrong people.
> Sebastian wrote:
> > My advice is: Stay within defined behavior and you have the cozy situation of not having to worry about
> >that all. If you want to hack at the interface between abstract and concrete machine, than you have to
> > embrace all those subtleties.
>
>
> I'm talking about repair jobs where the damage has already been done.
> I'm talking about compensating for a bug, or compensating for a
> deliberate avoidance to break ABI.
But you're not; your loop example is not a "repair job" where UB is
the only way forward. It's trivially easy to turn it into defined
behavior.
You just don't think you should have to.
> Jason wrote:
> > "Obviously intended"? What makes you say that? After all, if that were
> > the user's intent, then "obviously" they would write the C++ code that
> > would actually *do that*, rather than relying on UB:
> >
> > ```
> > for ( unsigned int i = 0; i < (unsigned
> > int)std::numeric_limits<int>::max(); ++i )
> > {
> > if ( SomeFunc((int)i) ) break;
> > }
> >
> > SomeFunc(-1);
> > }
> > ```
>
>
> I haven't done a poll but I reckon 9 out of 10 C++ programmers don't
> know that the compiler _might_ neglect to check if a positive number
> has incremented negatively.
I suspect there are a fair number of programmers who have no idea that
if you increment a signed integer enough, it will turn negative.
Especially those programmers who came from languages where such
nonsense does not happen. For them, the "obvious intent" of that code
is an infinite loop.
> Jason wrote:
> > That's why the committee standardized two's complement signed integers
> > to begin with.
>
>
> Maybe we should have a new keyword in the language: '_Fsigned'
>
> An integer type marked as '_Fsigned' would have defined behaviour
> where INT_MAX increments to INT_MIN.
We have that. It's called "unsigned int`. And since the conversion
between `int` and `unsigned int` is well-defined for all values of
both of those types, what would be the point of this other type?
> Jason wrote:
> > There's that phrase again: "obviously intended".
>
>
> When I say 'obviously intended', I mean read the code line by line and
> just see plainly what it's meant to do.
And that's the problem: "line by line" reading does not give you
"intention". It just tells you what the instructions are doing. The
assumption that a "line by line" reading of a program is an accurate
description of the programmer's "intent" (even when the program does
things that clearly are not legitimate) is precisely that: an
*assumption*.
Equally importantly, what does a "line by line" reading mean when the
standard does not allow for the thing the user "intended"? C++ as a
language does not have any circumstance where incrementing a positive,
signed integer will cause it to turn negative. So if you do a "line by
line" reading of the code in accord with the C++ standard, it's an
infinite loop. Period.
So where does your "obvious intent" come from when the very document
that defines what those lines *mean* says that it cannot happen?
Std-Proposals <std-proposals_at_[hidden]> wrote:
>
> I reply in series to Sebastian, Jason and Thiago below.
>
>
> Sebastian wrote:
> >
> > Not all instances of UB can be detected and not all instances of UB can be defined.
>
>
> I'm talk specifically about situations where the compiler makes the
> determination:
> "That would result in UB and I can therefore take certain
> liberties here, such as not bothering to check if the signed integer
> goes from positive to negative"
>
> So I'm talking specifically about situations where UB is detected.
But that's not really what's going on.
What's going on is that the compiler looks at your code and sees that
the only thing that changes `i` is the increment expression. And this
expression only ever increments the value. Incrementing a signed
integer cannot make it smaller. Therefore, the value only increases
from its original value. And therefore, the value can never become
less than its original value. The condition is thus trivially true.
No UB is being "detected". The compiler is simply not considering
behavior that the standard does not define as possible. Which is how
compilers *should work*. They should not consider things that cannot
be.
Lastly, you keep talking about "compiler" behavior. But you don't seem
aware that the standard cannot say the things you're trying to make it
say. It cannot coerce compiler behavior because the "behavior" in
question is *explicitly* outside of the boundaries that the standard
itself defines for C++.
This is a core problem of your idea: you're asking for a change from
the wrong people.
> Sebastian wrote:
> > My advice is: Stay within defined behavior and you have the cozy situation of not having to worry about
> >that all. If you want to hack at the interface between abstract and concrete machine, than you have to
> > embrace all those subtleties.
>
>
> I'm talking about repair jobs where the damage has already been done.
> I'm talking about compensating for a bug, or compensating for a
> deliberate avoidance to break ABI.
But you're not; your loop example is not a "repair job" where UB is
the only way forward. It's trivially easy to turn it into defined
behavior.
You just don't think you should have to.
> Jason wrote:
> > "Obviously intended"? What makes you say that? After all, if that were
> > the user's intent, then "obviously" they would write the C++ code that
> > would actually *do that*, rather than relying on UB:
> >
> > ```
> > for ( unsigned int i = 0; i < (unsigned
> > int)std::numeric_limits<int>::max(); ++i )
> > {
> > if ( SomeFunc((int)i) ) break;
> > }
> >
> > SomeFunc(-1);
> > }
> > ```
>
>
> I haven't done a poll but I reckon 9 out of 10 C++ programmers don't
> know that the compiler _might_ neglect to check if a positive number
> has incremented negatively.
I suspect there are a fair number of programmers who have no idea that
if you increment a signed integer enough, it will turn negative.
Especially those programmers who came from languages where such
nonsense does not happen. For them, the "obvious intent" of that code
is an infinite loop.
> Jason wrote:
> > That's why the committee standardized two's complement signed integers
> > to begin with.
>
>
> Maybe we should have a new keyword in the language: '_Fsigned'
>
> An integer type marked as '_Fsigned' would have defined behaviour
> where INT_MAX increments to INT_MIN.
We have that. It's called "unsigned int`. And since the conversion
between `int` and `unsigned int` is well-defined for all values of
both of those types, what would be the point of this other type?
> Jason wrote:
> > There's that phrase again: "obviously intended".
>
>
> When I say 'obviously intended', I mean read the code line by line and
> just see plainly what it's meant to do.
And that's the problem: "line by line" reading does not give you
"intention". It just tells you what the instructions are doing. The
assumption that a "line by line" reading of a program is an accurate
description of the programmer's "intent" (even when the program does
things that clearly are not legitimate) is precisely that: an
*assumption*.
Equally importantly, what does a "line by line" reading mean when the
standard does not allow for the thing the user "intended"? C++ as a
language does not have any circumstance where incrementing a positive,
signed integer will cause it to turn negative. So if you do a "line by
line" reading of the code in accord with the C++ standard, it's an
infinite loop. Period.
So where does your "obvious intent" come from when the very document
that defines what those lines *mean* says that it cannot happen?
Received on 2023-08-27 15:38:39