Post Reply 
Undoc'd Feature?
07-09-2022, 05:15 AM
Post: #21
RE: Undoc'd Feature?
(07-08-2022 08:32 PM)jte Wrote:  ... as C++17 introduces additional sequencing constraints involving assignment operators.

I tried the following on several different C/C++ compilers

Code:
int a=5;
a = a++;
printf("%d\n",a);

and they all printed out 5, except for the Microsoft compiler which prints 6. They all claim to be C++20 compliant, so somebody's wrong.
Find all posts by this user
Quote this message in a reply
07-09-2022, 12:20 PM
Post: #22
RE: Undoc'd Feature?
(07-09-2022 05:15 AM)Wes Loewer Wrote:  I tried the following on several different C/C++ compilers

Code:
int a=5;
a = a++;
printf("%d\n",a);

and they all printed out 5, except for the Microsoft compiler which prints 6. They all claim to be C++20 compliant, so somebody's wrong.

I would contend that the Microsoft compiler is wrong and the others are correct.

The effect of:

Code:
a = a++

...should be:

1) Keep a record of the current value of 'a' as this is going to be the value of the expression 'a++'

2) Do a post-increment on 'a', so 'a' is now 6

3) Assign the value of the expression 'a++' retained in step 1 (i.e.: 5) to 'a'

Step 3 re-assigns 5 to a.

Current daily driver: HP 71B w/FRAM 71B
Find all posts by this user
Quote this message in a reply
07-09-2022, 05:10 PM
Post: #23
RE: Undoc'd Feature?
(07-09-2022 12:20 PM)RPNerd Wrote:  I would contend that the Microsoft compiler is wrong and the others are correct.

There is no right or wrong. With Undefined Behavior, Anything is Possible

Your reasoning suggested {a=a++;} ≡ {t=a; a++; a=t;}, with a unchanged.
But, another way to look at this: {a=a; a++;} will have a incremented.
Find all posts by this user
Quote this message in a reply
07-10-2022, 05:22 AM
Post: #24
RE: Undoc'd Feature?
(07-09-2022 05:10 PM)Albert Chan Wrote:  There is no right or wrong. With Undefined Behavior, Anything is Possible

But what jte was saying is that this used to be undefined, but starting with C++17 it is now defined.

Here's an interesting side-note from digging through the C/C++ ISO/ANSI drafts. Even though this is defined in C++17, it is still not defined in Standard C17 or even in the working draft for C2x.
Find all posts by this user
Quote this message in a reply
07-10-2022, 10:22 AM
Post: #25
RE: Undoc'd Feature?
(07-09-2022 12:20 PM)RPNerd Wrote:  
(07-09-2022 05:15 AM)Wes Loewer Wrote:  I tried the following on several different C/C++ compilers

Code:
int a=5;
a = a++;
printf("%d\n",a);

and they all printed out 5, except for the Microsoft compiler which prints 6. They all claim to be C++20 compliant, so somebody's wrong.

I would contend that the Microsoft compiler is wrong and the others are correct.

The effect of:

Code:
a = a++

...should be:

1) Keep a record of the current value of 'a' as this is going to be the value of the expression 'a++'

2) Do a post-increment on 'a', so 'a' is now 6

3) Assign the value of the expression 'a++' retained in step 1 (i.e.: 5) to 'a'

Step 3 re-assigns 5 to a.

I'd think the sequence would be

1) store the current value of a (5) back in a
2) increment a

This would still give 5, though.

Tom L
Cui bono?
Find all posts by this user
Quote this message in a reply
07-10-2022, 12:48 PM
Post: #26
RE: Undoc'd Feature?
(07-10-2022 10:22 AM)toml_12953 Wrote:  I'd think the sequence would be

1) store the current value of a (5) back in a
2) increment a

This would still give 5, though.

Actually, no, it would yield 6 (store 5 in a and then increment a), which is possibly what the M$ compiler is doing. This is wrong, IMO, because the C/C++ standard clearly states that the result of an expression consisting of a post-incremented variable is the initial value of that variable. We are therefore totally justified in expecting "a = a++;" to assign to the variable a the value of the expression "a++", i.e.: the initial value of a. The evaluation should happen before the assignment.

If a M$ compiler (and only that compiler) is returning anything else then I think we can chalk it up to some kind of compiler optimisation producing yet another "feature, not a bug".

Current daily driver: HP 71B w/FRAM 71B
Find all posts by this user
Quote this message in a reply
07-10-2022, 05:36 PM
Post: #27
RE: Undoc'd Feature?
(07-09-2022 05:15 AM)Wes Loewer Wrote:  
(07-08-2022 08:32 PM)jte Wrote:  ... as C++17 introduces additional sequencing constraints involving assignment operators.

I tried the following on several different C/C++ compilers

Code:
int a=5;
a = a++;
printf("%d\n",a);

and they all printed out 5, except for the Microsoft compiler which prints 6. They all claim to be C++20 compliant, so somebody's wrong.

Is the combination of definition and assignment in one step new to C? The C interpreter in Casio calculators requires two steps:

int a;
a=5;

Tom L
Cui bono?
Find all posts by this user
Quote this message in a reply
07-10-2022, 05:57 PM
Post: #28
RE: Undoc'd Feature?
(07-10-2022 05:22 AM)Wes Loewer Wrote:  But what jte was saying is that this used to be undefined, but starting with C++17 it is now defined.

Defined to what? (a=a++) ⇒ NOP?

(07-10-2022 05:36 PM)toml_12953 Wrote:  Is the combination of definition and assignment in one step new to C?

It was always allowed, ever since K&R days.

The C Programming Language, 2nd Edition, page 86 Wrote:In effect, initializations of automatic variables are just shorthand for assignment statements. Which form to prefer is largely a matter of taste. We have generally used explicit assignments, because initializers in declarations are harder to see and further away from the point of use.
Find all posts by this user
Quote this message in a reply
07-10-2022, 07:38 PM (This post was last modified: 07-10-2022 07:39 PM by Wes Loewer.)
Post: #29
RE: Undoc'd Feature?
(07-10-2022 05:57 PM)Albert Chan Wrote:  Defined to what? (a=a++) ⇒ NOP?

The C++14 documentation gives this example
Code:
i = i++ + 1; // the behavior is undefined
i = i + 1; // the value of i is incremented

While the C++17 documentation has this

Code:
i = i++ + 1; // the value of i is incremented
i = i++ + i; // the behavior is undefined
i = i + 1; // the value of i is incremented

So if (i = i++ + 1) increments just like (i = i + 1), then it stands to reason that (i=i++) behaves like (i = i).

I looked at the g++ assembly output of (a=a++). Without optimization, the code copies a to itself. With optimization, the line is ignored. Also with optimization, (a = a++ + 1) compiled exactly the same as (a=a+1) and (a++), consistent with the C++17 documentation.
Find all posts by this user
Quote this message in a reply
07-11-2022, 07:49 PM (This post was last modified: 07-11-2022 07:56 PM by ijabbott.)
Post: #30
RE: Undoc'd Feature?
(07-10-2022 07:38 PM)Wes Loewer Wrote:  While the C++17 documentation has this

Code:
i = i++ + 1; // the value of i is incremented
i = i++ + i; // the behavior is undefined
i = i + 1; // the value of i is incremented

So if (i = i++ + 1) increments just like (i = i + 1), then it stands to reason that (i=i++) behaves like (i = i).

Not really. For `i = i++ + 1;`, the `i++` part says that the old value of `i` plus 1 will be be stored in `i`, and the `i = i + 1` part also says that the old value of `i` plus 1 will be stored in `i`, so there is no conflict (in C++17, but undefined in C17). However, for `i = i++;`, the `i++` part says that the old value of `i` plus 1 will be stored in `i`, and the `i = i` part says that the old value of `i` will be stored in `i`, so there is a conflict. That's also one reason why `i = i++ + i;` is undefined - the `i++` part says the old value of `i` plus 1 will be stored in `i`, and the `i = i + i` part says the old value of `i` plus the old value of `i` will be stored in `i`. (The other reason is that the storage to `i` by `i++` is unsequenced relative to the evaluation of `i` in the `... + i`.)

— Ian Abbott
Find all posts by this user
Quote this message in a reply
07-12-2022, 11:03 AM (This post was last modified: 07-12-2022 11:04 AM by RPNerd.)
Post: #31
RE: Undoc'd Feature?
(07-11-2022 07:49 PM)ijabbott Wrote:  However, for `i = i++;`, the `i++` part says that the old value of `i` plus 1 will be stored in `i`, and the `i = i` part says that the old value of `i` will be stored in `i`, so there is a conflict.

I see no conflict there.

in:

Code:
i = i++;

...as in any assignment, the rhs is evaluated so that the result can be assigned to the lhs variable, which just happens to be the same variable in this particular case.

"i++" is evaluated as the initial value of i. A by-product of that evaluation is post-incrementing i but that doesn't matter because the initial value is then assigned back to i.

Basically, the result is a big fat NOP with a few CPU cycles wasted incrementing i and then setting it back to its original value unless the compiler catches this.

Current daily driver: HP 71B w/FRAM 71B
Find all posts by this user
Quote this message in a reply
07-12-2022, 01:49 PM (This post was last modified: 07-12-2022 01:50 PM by Wes Loewer.)
Post: #32
RE: Undoc'd Feature?
(07-11-2022 07:49 PM)ijabbott Wrote:  Not really. For `i = i++ + 1;`, the `i++` part says that the old value of `i` plus 1 will be be stored in `i`, and the `i = i + 1` part also says that the old value of `i` plus 1 will be stored in `i`, so there is no conflict (in C++17, but undefined in C17).

So are you saying that (i = i++ + 2) would also be undefined since it is ambiguous whether i gets incremented by 1 or by 2?

My (perhaps flawed) understanding was that this is defined to follow the following sequence. Say i=5 to start.
1) i is evaluated to 5
2) 2 is added to 5 to get 7
3) i is incremented to 6
4) the value of 7 is stored in i

Or maybe steps 2 and 3 might be reversed, but in either case, i ends up equaling 7, so compilers are free to ignore the ++.

(07-11-2022 07:49 PM)ijabbott Wrote:  (The other reason is that the storage to `i` by `i++` is unsequenced relative to the evaluation of `i` in the `... + i`.)

I'm certainly no expert in these matters, but my understanding was that this is the only reason that (i = i++ + i) was undefined.
Find all posts by this user
Quote this message in a reply
07-13-2022, 12:44 PM
Post: #33
RE: Undoc'd Feature?
This has been an interesting read and discussion.

It seems to be the best thing to do would not be this construct, since the results are ambiguous, and cannot be depended on as the programmer potentially migrates code.

be careful, folks, and document your code.
Find all posts by this user
Quote this message in a reply
07-13-2022, 08:01 PM (This post was last modified: 07-13-2022 08:01 PM by Wes Loewer.)
Post: #34
RE: Undoc'd Feature?
(07-13-2022 12:44 PM)ctrclckws Wrote:  It seems to be the best thing to do would not be this construct, since the results are ambiguous, and cannot be depended on as the programmer potentially migrates code.

Indeed. My interest in this discussion is in learning the exact rules of the game, so to speak. I think it's important to know what could be done as well as what should be done. It's like learning the rules of a game as well as the best strategies of the game.

It is also important to be able to understand code that someone else has written that might use such a construct.
Find all posts by this user
Quote this message in a reply
07-13-2022, 09:34 PM (This post was last modified: 07-13-2022 10:30 PM by ijabbott.)
Post: #35
RE: Undoc'd Feature?
(07-12-2022 11:03 AM)RPNerd Wrote:  
(07-11-2022 07:49 PM)ijabbott Wrote:  However, for `i = i++;`, the `i++` part says that the old value of `i` plus 1 will be stored in `i`, and the `i = i` part says that the old value of `i` will be stored in `i`, so there is a conflict.

I see no conflict there.

in:

Code:
i = i++;

...as in any assignment, the rhs is evaluated so that the result can be assigned to the lhs variable, which just happens to be the same variable in this particular case.

"i++" is evaluated as the initial value of i. A by-product of that evaluation is post-incrementing i but that doesn't matter because the initial value is then assigned back to i.

EDIT: IGNORE THE FOLLOWING - IT IS WRONG

The problem is that the two stores to `i` are unsequenced relative to each other. You are under the impression that the store caused by `++` on the r.h.s. occurs before the store caused by the `=`, but there is nothing in the standard to support that.

TBH, I don't know why they bothered allowing `i = i++ + 1;` as a special case as it is a pretty pointless construct. The blanket ban in the earlier version is easier to understand, IMHO.

EDIT: CORRECTION FOLLOWS

I missed the sentence added to C++17 for the assignment operators that reads as follows
Quote:The right operand is sequenced before the left operand.

That little sentence makes all the difference!

— Ian Abbott
Find all posts by this user
Quote this message in a reply
07-13-2022, 10:12 PM (This post was last modified: 07-13-2022 10:32 PM by ijabbott.)
Post: #36
RE: Undoc'd Feature?
(07-12-2022 01:49 PM)Wes Loewer Wrote:  
(07-11-2022 07:49 PM)ijabbott Wrote:  Not really. For `i = i++ + 1;`, the `i++` part says that the old value of `i` plus 1 will be be stored in `i`, and the `i = i + 1` part also says that the old value of `i` plus 1 will be stored in `i`, so there is no conflict (in C++17, but undefined in C17).

So are you saying that (i = i++ + 2) would also be undefined since it is ambiguous whether i gets incremented by 1 or by 2?

My (perhaps flawed) understanding was that this is defined to follow the following sequence. Say i=5 to start.
1) i is evaluated to 5
2) 2 is added to 5 to get 7
3) i is incremented to 6
4) the value of 7 is stored in i

Or maybe steps 2 and 3 might be reversed, but in either case, i ends up equaling 7, so compilers are free to ignore the ++.

EDIT: IGNORE THE FOLLOWING - IT IS WRONG (see my post just above this one)

At some point 7 is stored in `i` and at some other point `i` is modified by adding 1 to it. Those two points are unsequenced relative to each other so the behaviour is undefined. Maybe `i` is incremented to 6 before 7 is stored in `i`, or maybe 7 is stored in `i` before `i` is incremented to 8, or (since the behaviour is undefined) maybe 7 is stored in `i` before 1 is added to the old value (5) of `i` and stored in `i`.

— Ian Abbott
Find all posts by this user
Quote this message in a reply
07-13-2022, 10:50 PM
Post: #37
RE: Undoc'd Feature?
(07-13-2022 10:12 PM)ijabbott Wrote:  
(07-12-2022 01:49 PM)Wes Loewer Wrote:  So are you saying that (i = i++ + 2) would also be undefined since it is ambiguous whether i gets incremented by 1 or by 2?

My (perhaps flawed) understanding was that this is defined to follow the following sequence. Say i=5 to start.
1) i is evaluated to 5
2) 2 is added to 5 to get 7
3) i is incremented to 6
4) the value of 7 is stored in i

Or maybe steps 2 and 3 might be reversed, but in either case, i ends up equaling 7, so compilers are free to ignore the ++.

EDIT: IGNORE THE FOLLOWING - IT IS WRONG (see my post just above this one)

At some point 7 is stored in `i` and at some other point `i` is modified by adding 1 to it. Those two points are unsequenced relative to each other so the behaviour is undefined. Maybe `i` is incremented to 6 before 7 is stored in `i`, or maybe 7 is stored in `i` before `i` is incremented to 8, or (since the behaviour is undefined) maybe 7 is stored in `i` before 1 is added to the old value (5) of `i` and stored in `i`.

No wonder it isn't recommended to use autoincrement/decrement in complex instructions!

Tom L
Cui bono?
Find all posts by this user
Quote this message in a reply
07-14-2022, 05:22 PM
Post: #38
RE: Undoc'd Feature?
(07-09-2022 12:20 PM)RPNerd Wrote:  
(07-09-2022 05:15 AM)Wes Loewer Wrote:  I tried the following on several different C/C++ compilers

Code:
int a=5;
a = a++;
printf("%d\n",a);

and they all printed out 5, except for the Microsoft compiler which prints 6. They all claim to be C++20 compliant, so somebody's wrong.

I would contend that the Microsoft compiler is wrong and the others are correct.

This is ironic given that Microsoft proposed the new rules in the first place! See accepted paper P0145R3.

— Ian Abbott
Find all posts by this user
Quote this message in a reply
07-17-2022, 01:05 PM
Post: #39
RE: Undoc'd Feature?
(07-10-2022 12:48 PM)RPNerd Wrote:  Actually, no, it would yield 6 (store 5 in a and then increment a), which is possibly what the M$ compiler is doing. This is wrong, IMO, because the C/C++ standard clearly states that the result of an expression consisting of a post-incremented variable is the initial value of that variable. We are therefore totally justified in expecting "a = a++;" to assign to the variable a the value of the expression "a++", i.e.: the initial value of a. The evaluation should happen before the assignment.

If a M$ compiler (and only that compiler) is returning anything else then I think we can chalk it up to some kind of compiler optimisation producing yet another "feature, not a bug".

It does not seem to be an optimization issue. Even without optimizations turned on, the compiler gives the same results.

Here's a simple, intentionally undefined example that indicates what each compiler is probably doing.

Code:
a = 5;
a = 1000*a++ + 100*a++ + 10*a++;  // undefined expression.
Note: Since this is undefined, there is no guarantee that the results are indicative of the results a compiler would produce in a defined case, but it does suggest what a compiler might be doing. That said, here are some results:

gcc/clang/intel all print 5670
ms prints 5553

So gcc/clang/intel are evaluating then incrementing each time left to right, while MS doesn't process the three ++'s until after the = assignment.

(07-14-2022 05:22 PM)ijabbott Wrote:  This is ironic given that Microsoft proposed the new rules in the first place! See accepted paper P0145R3.

Very ironic indeed. A good read though.
Find all posts by this user
Quote this message in a reply
07-31-2022, 01:29 AM (This post was last modified: 07-31-2022 02:17 AM by jte.)
Post: #40
RE: Undoc'd Feature?
(07-09-2022 05:15 AM)Wes Loewer Wrote:  
(07-08-2022 08:32 PM)jte Wrote:  … as C++17 introduces additional sequencing constraints involving assignment operators.

I tried the following on several different C/C++ compilers

Code:
int a=5;
a = a++;
printf(“%d\n”,a);

and they all printed out 5, except for the Microsoft compiler which prints 6. They all claim to be C++20 compliant, so somebody’s wrong.

After reading this, I was meaning to reply, but wanted to first try your code out with a Microsoft compiler myself. While I have been plugging away at some C++ code, that’s been with a Mac here; I have another machine here, running Windows, that has Microsoft compilers installed, but it’s been warm enough [and I've been busy enough…] that I’ve not wanted to boot up multiple heat-spewing desktops simultaneously. (For the past week I’ve also been wearing 95PFE masks almost all the time [just not when eating or brushing teeth…] as a family member has tested positive for covid [but has had only mild symptoms], which certainly doesn’t lessen my perception of the summer heat. [Before this, I assumed the current variants are contagious enough that preventing spread between people living in the same home, sharing a bathroom, etc. was essentially impossible — even with masks etc., but so far I’ve tested negative each day. I had some scientific curiosity as to what would unfold if we maximized mask wearing, and also had some concerns over the uncertainty of “long covid”, so decided to try wearing masks as much as possible.])

To try out some compilations, I placed your sample code into a function, as follows:
Code:
int Wes(int a)
{
a = 5;
a = a++;
return a;
}

When I try that function out with MSVC++ v14.28 / 1928 (x64), optimizations on, I get the following:
Code:
int Wes(int) PROC
        mov     eax, 6
        ret     0
int Wes(int) ENDP

But, if I add “/std:c++17” as a command-line option, I instead get the following:
Code:
int Wes(int) PROC
        mov     eax, 5
        ret     0
int Wes(int) ENDP

So it seems that conformance with this part of the C++17 language does depend on the options given to the Microsoft compiler that I tried.
Find all posts by this user
Quote this message in a reply
Post Reply 




User(s) browsing this thread: 1 Guest(s)