Why does C++17 GCC compiler gives warning about undefined? - c++

According to C++17, there is no guarantee for order of evaluation in following expression. It is called unspecified behaviour.
int i = 0;
std::cout<<i<<i++<<std::endl;
C++17 GCC compiler gives following warning: Live Demo
prog.cc: In function 'int main()':
prog.cc:6:20: warning: operation on 'i' may be undefined [-Wsequence-point]
std::cout<<i<<i++<<std::endl;
I don't understand, in c++17 above express no longer undefined behaviour, then Why does compiler gives warning about undefined?

Seems like gcc gives a warning because this is a corner case, or at least very close to being one. Portability seems to be one concern.
From the page https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
The C++17 standard will define the order of evaluation of operands in more cases: in particular it requires that the right-hand side of an assignment be evaluated before the left-hand side, so the above examples are no longer undefined. But this warning will still warn about them, to help people avoid writing code that is undefined in C and earlier revisions of C++.
The standard is worded confusingly, therefore there is some debate over the precise meaning of the sequence point rules in subtle cases. Links to discussions of the problem, including proposed formal definitions, may be found on the GCC readings page, at http://gcc.gnu.org/readings.html.

Related

Is `x^=y^=x^=y;` correct for swapping integers in C/C++?

x^=y^=x^=y; is a tricky/amusing implementation of the XOR swap algorithm in C and C++. It parses as x^=(y^=(x^=y)); and uses the fact that assignment operators return the assigned value. But is it correct? The GCC 10.3.0 C compiler gives me the warning operation on ‘x’ may be undefined [-Wsequence-point] and clang 12.0.0 warning: unsequenced modification and access to 'x' [-Wunsequenced]. Compiling as C++, clang continues to warn the same way, and GCC stops. So is this code correct in either language? It looks rather sequenced to me, but maybe it's illegal to modify a variable two times in the same statement?
As pointed out in this answer, clang++ -std=c++17 does not give the warning. With -std=c++11 the situation is as described above. So maybe my question should be further broken down into C/C++11/C++17.
Add --std=c++17 to your compiler and you will not get warning anymore.
There is a part that is added to C++17 that prevents undefined behavior and you need that part for it:
In every simple assignment expression E1=E2 and every compound assignment expression E1#=E2, every value computation and side-effect of E2 is sequenced before every value computation and side effect of E1
Though, I suggest that you never use it in your code too.

Why is calling the main function supposedly undefined behavior (UB)

I fear this is again a question about interpreting the ISO/IEC 14882 (the C++ standard) but:
Is calling main from the program e.g. my calling main() recursively from main not at least implementation defined behavior? (Update: I imply later ill-formed not implementation defined, also not UB, see below and answer)
6.9.3.1 [basic.start.main] states
3 The function main shall not be used within a program. The linkage (6.6) of main is implementation-defined...
The consensus seems to be undefined behavior (UB). The documentation of MSVC also points towards UB, the one of gcc also implicitly denies implementation-defined behavior. It can not be [defns.unspecified] unspecified behavior since I would interpret shall not as ill-formed.
However, despite the implementations, to my interpretation is should not be UB but as 4.1 [intro.compliance] states
1 The set of diagnosable rules consists of all syntactic and semantic rules in this document except for those
rules containing an explicit notation that “no diagnostic is required” or which are described as resulting in
“undefined behavior”.
...
(2.2) — If a program contains a violation of any diagnosable rule or an occurrence of a construct described in
this document as “conditionally-supported” when the implementation does not support that construct,
a conforming implementation shall issue at least one diagnostic message.
For me the reasoning seems clear
tl;dr
calling main implies the program contains a violation of the rule of [basic.start.main]
[basic.start.main] does not state calling/use is UB or a diagnostic is not required
is an element of "diagnosable rules" as per [intro.compliance]
[intro.compliance] 2.2 states violation of any diagnoseable rule must be issued at least one diagnostic message
Since 3. and 4. the usage of main shall issue at least one diagnostic message
Since 5. 1. is not UB
Since neither gcc,MSVC or clang issue an error or warning but compile, all major implementations are not compliant
Of course since 7. I feel again in the Don Quixote scenario i.e. being wrong, so I would appreciate if someone could enlighten me about my mistake. Otherwise, there are standard defects, aren't there?
I think your analysis is correct: calls to main are ill-formed.
You have to pass the -pedantic flag to make GCC and Clang conform. In that case, Clang says
warning: ISO C++ does not allow 'main' to be used by a program [-Wmain]
and GCC says
warning: ISO C++ forbids taking address of function '::main' [-Wpedantic]
But they allow calls to main as an extension. The standard permits such an extension, since it doesn't change the meaning of any conforming programs.

C++ Perincrement Undefined Operation vs C

I have this line of code:
front = (++front) % size;
In C I get no warnings but in C++ I get the warning operation on front may be undefined [-Wsequence-point]. How does this preincrement usage cause undefined behavior? In my mind, this line is very unambiguous and will be interpreted as:
increment front
mod front with size
assign new value to front.
Is my compiler just throwing a blanket warning?
P.S. I understand the warning if I were doing something like front = front++; or Heaven forbid front = front++ + front++;.
EDIT: This warning was produced in CodeBlocks on Windows 64 using GCC (tdm-1) 4.6.1
You are changing front twice between sequence points: once through ++, and once through assignment.
This is undefined behaviour.
In C++11 this is well-defined; the structure is the same as that of:
i = ++i + 1;
which is given as an example of well-defined behaviour in the Standard itself. For a more detailed explanation see AndreyT's answer here.
In C++03, C89 and C99 this is undefined behaviour as they had looser sequencting rules for ++i.
The old sequencing rules had edge cases where order of operations was well defined but which were still technically undefined behavior. With C++11 and C11 this has been fixed by replacing the sequence point requirements with 'sequenced-before' and 'sequenced-after' relations.
Your example happens to be such a case. If you're getting warnings in a C11 or C++11 mode then the warning simply hasn't been updated for the new rules yet. In earlier C and C++ modes the warning is correct. If you're not getting warnings in earlier modes then they simply weren't implemented, and that's okay as 'no diagnostic is required'.
At the same time, this line can be written more clearly and also be correct under the old rules:
front = (front + 1) % size;
Writing the incremented value back to front can happen at any time (before or after the assignment modifies front), so the warning is valid and the code is unsafe.

Why does gcc warn about decltype(main()) but not clang?

Take the following code:
int main()
{
decltype(main()) x = 0;
return x;
}
gcc complains:
main.cpp: In function 'int main()':
main.cpp:8:19: warning: ISO C++ forbids taking address of function '::main' [-Wpedantic]
decltype(main()) x = 0;
^
main.cpp:8:19: warning: ISO C++ forbids taking address of function '::main' [-Wpedantic]
but not clang. So what about decltype(main()) raises this error? How does decltype take the address of main?
GCC's diagnostic might not be correctly phrased in this case, because decltype doesn't need to know the address of main; it only needs to know its type. However, the warning is based on the following from the standard (§3.6.1/3):
The function main shall not be used within a program.
I suppose GCC interprets this to mean that you can't even use it in an unevaluated expression.
Clang (version 3.4 anyway) appears to not implement this rule at all, even if I turn on all the flags I can think of and even if main calls itself recursively. That's why it doesn't give you a warning.
This topic actually came up recently in the undefined behaviour study group discussion list in the thread What does "The function main shall not be used within a program" mean?. It does not come up right away but here is where it starts in the thread with the following statement:
I don't think decltype(main()) is an odr-use, or
sizeof(decltype(main)).
a very abbreviated set of responses looks like this:
True, I just don’t see what utility those would be. You might mean
sizeof(decltype(&main)) in the latter case.
I think the most common non-ODR use of main would be defining it after
a forward declaration, and now Steven Clamage has clarified that
should be ill-formed. The broader definition of “use” as being a
result of name lookup without reference to ODR looks correct now.
and:
C++98's mention of 'use' had a cross-reference to 3.2 [basic.def.odr].
C++11 no longer has the cross-reference, and was not changed to say
'odr-use', so I expect it means any use.
and so it would seem that the interpretation of section 3.6.1 Main function which says:
The function main shall not be used within a program. [...]
means any use even in unevaluated contexts and so gcc is correct here to produce an error although the message itself does not seem to make sense.
Update
It is interesting and instructive to note that the original proposal: N3154 to fix Defect report 1109 would have changed 3.6.1 to:
The function main shall not be odr-used (3.2) within a program. ...
which would have allowed the decltype example but was amended when accepted and we can see that the new proposal: N3214 changed to what we have today:
The function main shall not be used within a program
which would strongly indicate the opinion in the UB mailing list that any use of main is ill-formed is indeed correct.

Compiler chosing prefix ++ when postfix is missing - who says?

When you define a prefix operator++ for your user defined type and you don't provide a postfix version, the compiler (in Visual C++ at least) will use the PREFIX version when your code calls the missing POSTFIX version.
At least it will give you a warning. But, my question is: Why doesn't it just give you an error for the undefined member function?
I have seen this first hand, and have seen it mentioned in another post and elsewhere, but I cannot find this in the actual C++ standard. My second and third questions are... Is it in the standard somewhere? Is this a Microsoft-specific handing of the situation?
Actually, In this case the MSVC behaves much more intelligently than GCC.
This is a MSVC compiler extension and the C++ standard explicitly allows for such an behavior.
C++ Standard:
Section 1.4/8:
A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any well-formed program. Implementations are required to diagnose programs that use such extensions that are ill-formed according to this International Standard. Having done so, however, they can compile and execute such programs.
In this case, MSVC appropriately diagnoses the problem that the postfix is not available and it specifically defines warnings,
Compiler Warning (level 1) C4620
Compiler Warning (level 1) C4621
Also, it provides you a facility to disable the MSVC specific extensions by using /Za. Overall, I would say this is one of the instances where MSVC actually behaves better than GCC.
It should be a Microsoft specific extension. Because, at least g++ is strict about prefix and postfix operators. Here is the demo link.
With integers, ++i is different from i++. For exampe i=5, y = ++i - y will be 6; y=i++ - y will be 5. Other types should function in the same manner. Therefore the behaviour differs between postfix and prefix.
It is a Microsoft thing and in my opinion the compilers implementation is incorrect.