Should the conditional operator evaluate all arguments? - c++

When writing this:
1: inline double f( double arg ) {
2: return arg == 0.0 ? 0.0 : 1./arg;
3: }
4: const double d = f( 0.0 );
The microsoft visual studio 2005 64-bit compiler came with
line 4: warning C4723: potential divide by 0
While you and I can clearly see that a div-by-zero is never going to happen...
Or is it?

The compiler is not able to statically analyze all code paths and take into account all possibilities all the time. Theoretically, complete analysis of a program behavior just by looking into its source code can provide a solution to halting problem, which is undecidable. Compilers have a limited set of static analysis rules to detect rules. The C++ standard does not require the compiler to issue such kind of warnings, so, no. It's not a bug. It's more like a nonexistent feature.

No, the conditional operator does not evaluate both arguments. However, a potential divide-by-zero, if a compiler can detect such a thing, is typically reported. It is not for nought that the standard takes up ~2 pages to describe the behavior of this operator.
From N-4411:
5.16 Conditional operator
1 Conditional expressions group
right-to-left. The first expression is
contextually converted to bool (Clause
4). It is evaluated and if it is true,
the result of the conditional
expression is the value of the second
expression, otherwise that of the
third expression. Only one of the
second and third expressions is
evaluated. Every value computation and
side effect associated with the first
expression is sequenced before every
value computation and side effect
associated with the second or third
expression.
Also, note:
3 Otherwise, if the second and third
operand have different types, and
either has (possibly cv-qualified)
class type, an attempt is made to
convert each of those operands to the
type of the other.
The example you have cited has the same type for both the second and the third expressions -- rest assured, only the first will be evaluated.

It's an obvious bug, beyond doubt.
The intent of the warning is NOT to warn about all divisions in a program. That would be far too noisy in any reasonable program. Instead, the intent is to warn you when you need to check an argument. In this case, you did check the argument. Hence, the compiler should have noted that, and shut up.
The technical implementation of such a feature is done by labelling variables in code branches with certain attributes. One of the most common attributes is the tri-state "Is null". Before the branch, arg is an external variable and arg [[Isnull]] is unknown. But after the check on arg there are two branches. In the first branch arg [[Isnull]] is true. In the second branch arg [[Isnull]] is false.
Now, when it comes to generating divide-by-zero and null pointer warnings, the [[IsNull] attribute should be checked. If true, you have a severe warning/error. If unknown, you should generate the warning shown above - a potential problem, beyond what the compiler can prove. But in this case, the [[isNull]] attribute is False. The compiler by the same formal logic as humans, knows that there is no risk.
But how do we know that the compiler is using such an [[Isnull]] attribute internally? Recall the first paragraph : without it, it would have to either warn always or never. We know it warns sometimes, ergo there must be an [[IsNull]] attribute.

the code for the division will be generated, hence the warning. but the branch will never be taken when arg is 0, so it is safe.

operator== for floating-point numbers is unsafe (i.e. you cannot trust it, due to rounding issues). In this specific case it is actually safe, so you can ignore the warning, but the compiler will not make such an analysis based on an operator whose results are somewhat unpredictable in the general case.

The conditional operator shouldn't evaluate all arguments. But I believe you could take arg almost equal to 0, so arg == 0.0 will be false, but 1./arg will give "division by zero" result. So I think that warning is useful here.
By the way, Visual C++ 2008 doesn't give such warning.

In addition to the other comments: the warning is generate by the compiler, the dead branch is removed by the optimizer which runs later - possibly even at link stage.
So no, it's not a bug. The warning is an additional service provided by the compiler, not mandated by the standard. It's an unfortunate side effect of the compiler / linker architecture.

You might be able to avoid the warning using the Microsoft-specific __assume keyword. I'm not sure if you can tie it in with the conditional operator. Otherwise something like
if (arg == 0.0){
return 0.0;
}
else {
__assume(arg != 0.0);
return 1./arg;
}
may be worth a shot. Or, of course, just silence the warning during this function, with the appropriate #pragma.

Related

Is nodiscard necessary on operators?

Is the [[nodiscard]] attribute necessary on operators? Or is it safe to assume the compiler will emit a warning like it does for most suspiciously discarded things?
E.g. an overloaded operator+, should one apply the attribute? What about special operators like function-cast operators or new operators? When is it pedantic?
Let me cite the following paper by N.Josuttis: "[[nodiscard]] in the library" (with some omissions, see the full paper):
C++17 introduced the [[nodiscard]] attribute. The question is, where to apply it now in the standard library. It should be added where:
not using the return value always is a “huge mistake” (e.g. always resulting in resource leak),
not using the return value is a source of trouble and easily can happen (not obvious that something is wrong).
It should not be added when:
not using the return value is a possible/common way of programming at least for some input,
not using the return value makes no sense but doesn’t hurt and is usually not an error.
So, [[nodiscard]] should not signal bad code if this
can be useful not to use the return value,
is common not to use the return value,
doesn’t hurt and probably no state change was meant that doesn’t happen.
It is never necessary to add the [[nodiscard]] attribute. From cppreference:
If a function declared nodiscard or a function returning an enumeration or class declared nodiscard by value is called from a discarded-value expression other than a cast to void, the compiler is encouraged to issue a warning.
Note the last part: "... the compiler is encouraged to issue a warning." The is no guarantee, as far as the standard is concerned, that there actually will be a warning. Its a quality of implementation issue. If your compiler does emit a warning (read the docs) and if you are treating such warnings as errors, then the [[nodiscard]] can be of great use.
It is pedantic to use the attribute on operators where discarding the return is only potentially an error. I would only use it when calling the operator and discarding the result is always a logic error. Many operators use the return value merely to enable chaining and the [[nodiscard]] would rather be an annoyance on such operators. There are cases where the decision is not so obvious and it is a matter of opinion and style what you choose.
Is nodiscard necessary on operators?
No. nodiscard and other attribures are optional.
Or is it safe to assume the compiler will emit a warning like it does for most suspiciously discarded things?
There is no guarantee about any warning in the language except when the program is ill formed.
I would also not assume warning without nodiscard because there are many cases where result of operation is intentionally discarded. A common example:
a = b; // result of assignment was discarded
In fact, if all discarded results resulted in a warning, then there would not be any purpose for the nodiscard attribure.

Why gcc and clang both don't emit any warning?

Suppose we have code like this:
int check(){
int x = 5;
++x; /* line 1.*/
return 0;
}
int main(){
return check();
}
If line 1 is commented out and the compiler is started with all warnings enabled, it emits:
warning: unused variable ‘x’ [-Wunused-variable]
However if we un-comment line 1, i.e. increase x, then no warning is emitted.
Why is that? Increasing the variable is not really using it.
This happen in both GCC and Clang for both c and c++.
Yes.
x++ is the same as x = x+1;, the assignment. When you are assigning to something, you possibly can not skip using it. The result is not discarded.
Also, from the online gcc manual, regarding -Wunused-variable option
Warn whenever a local or static variable is unused aside from its declaration.
So, when you comment the x++;, it satisfies the condition to generate and emit the warning message. When you uncomment, the usage is visible to the compiler (the "usefulness" of this particular "usage" is questionable, but, it's an usage, nonetheless) and no warning.
With the preincrement you are incrementing and assigning the value to the variable again. It is like:
x=x+1
As the gcc documentation says:
-Wunused-variable:
Warn whenever a local or static variable is unused aside from its declaration.
If you comment that line you are not using the variable aside of the line in which you declare it
increasing variable not really using it.
Sure this is using it. It's doing a read and a write access on the stored object. This operation doesn't have any effect in your simple toy code, and the optimizer might notice that and remove the variable altogether. But the logic behind the warning is much simpler: warn iff the variable is never used.
This has actually the benefit that you can silence that warning in cases where it makes sense:
void someCallback(void *data)
{
(void)data; // <- this "uses" data
// [...] handler code that doesn't need data
}
Why is that? increasing variable not really using it.
Yes, it is really using it. At least from the language point of view. I would hope that an optimizer removes all trace of the variable.
Sure, that particular use has no effect on the rest of the program, so the variable is indeed redundant. I would agree that warning in this case would be helpful. But that is not the purpose of the warning about being unused, that you mention.
However, consider that analyzing whether a particular variable has any effect on the execution of the program in general is quite difficult. There has to be a point where the compiler stops checking whether a variable is actually useful. It appears that the stages that generate warnings of the compilers that you tested only check whether the variable is used at least once. That once was the increment operation.
I think there is a misconception about the word 'using' and what the compiler means with that. When you have a ++i you are not only accessing the variable, you are even modifying it, and AFAIK this counts as 'use'.
There are limitations to what the compiler can identify as 'how' variables are being used, and if the statements make any sense. In fact both clang and gcc will try to remove unnecessary statements, depending on the -O-flag (sometimes too aggressively). But these optimizations happen without warnings.
Detecting a variable that is never ever accessed or used though (there is no further statement mentioning that variable) is rather easy.
I agree with you, it could generate a warning about this. I think it doesn't generate a warning, because developers of the compilers just didn't bothered handling this case (yet). Maybe it is because it is too complicated to do. But maybe they will do this in the future (hint: you can suggest them this warning).
Compilers getting more and more warnings. For example, there is -Wunused-but-set-variable in GCC (which is a "new" warning, introduced in GCC 4.6 in 2011), which warns about this:
void fn() {
int a;
a = 2;
}
So it is completely fine to expect that this emits a warning too (there is nothing different here, neither codes do anything useful):
void fn() {
int a = 1;
a++;
}
Maybe they could add a new warning, like -Wmeaningless-variable
As per C standard ISO/IEC 9899:201x, expressions evaluation are always executed to allow for expression's side effects to be produced unless the compiler can't be sufficiently sure that removing it the program execution is not altered.
5.1.2.3 Program execution
In the abstract machine, all expressions are evaluated as specified by the semantics. An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object).
When removing the line
++x;
The compiler can deduce that the local variable x is defined and initialized, but not used.
When you add it, the expression itself can be considered a void expression, that must be evaluated for side effects, as stated in:
6.8.3 Expression and null statements
The expression in an expression statement is evaluated as a void expression for its side effects.
On the other hand to remove compiler warnings relative to unused variable is very common to cast the expression to void. I.e. for an unused parameter in a function you can write:
int MyFunc(int unused)
{
(void)unused;
...
return a;
}
In this case we have a void expression that reference the symbol unused.

"control reaches end of non-void function" with fully handled case switch over an enum type

Why does this code trigger a "control reaches end of non-void function" even if all possible values of type_t are handled? What is the best way to take care of this warning? Adding a return -1 after the switch?(Code tested here)
typedef enum {
A,
B
} type_t;
int useType(type_t x) {
switch (x) {
case A:
return 0;
case B:
return 1;
}
}
Related: Detecting if casting an int to an enum results into a non-enumerated value
In general, enums are not exclusive. Someone could call your function like useType( (type_t)3 ); for example. This is mentioned specifically in C++14 [dcl.enum]/8:
It is possible to define an enumeration that has values not defined by any of its enumerators.
Now, there are a bunch of rules about exactly which other values are possible for which other sorts of enum.
There are two categories of enum. The first is fixed underlying type, e.g. enum type_t : int, or enum class type_t . In those cases all values of the underlying type are valid enumerators.
The second is not fixed underlying type, which includes pre-C++11 enums such as yours. In this case the rule about values can be summarized by saying: compute the smallest number of bits necessary in order to store all values of the enum; then any number expressible in that number of bits is a valid value.
So - in your specific case, a single bit can hold both values A and B, so 3 is not valid value for the enumerator.
But if your enum were A,B,C, then even though 3 is not listed specifically, it is a valid value by the above rule. (So we can see that almost all enums will not be exclusive).
Now we need to look at the rule for what happens if someone does actually try to convert 3 to type_t. The conversion rule is C++14 [expr.static.cast]/10, which says that an unspecified value is produced.
However, CWG issue 1766 recognized that the C++14 text was defective and has replaced it with the following:
A value of integral or enumeration type can be explicitly converted to a complete enumeration type. The value is unchanged if the original value is within the range of the enumeration values (7.2). Otherwise, the behavior is undefined.
Therefore, in your specific case of exactly two enumerators with value 0 and 1, no other value is possible unless the program has already triggered undefined behaviour, so the warning could be considered a false positive.
To remove the warning, add a default: case that does something. I'd also suggest, in the interest of defensive programming, that it's a good idea to have a default case anyway. In practice it may serve to 'contain' the undefined behaviour: if someone does happen to pass an invalid value then you can throw or abort cleanly.
NB: Regarding the warning itself: it's impossible for a compiler to accurately warn if and only if control flow would reach the end of a function , because this would require solving the halting problem.
They tend to err on the side of caution: the compiler will warn if it is not completely sure, meaning that there are false positives.
So the presence of this warning does not necessarily indicate that the executable would actually allow entry into the default path.
To answer the second question ("What is the best way to take care of this warning?"):
In my eyes, typically, the best method is to add a call to __builtin_unreachable() after the switch statement (available both in GCC and Clang - maybe at some point we'll get [[unreachable]]).
This way, you explicitly tell the compiler that the code never runs over the switch statement. And if it does, you are happy to accept all dire consequences of undefined behaviour. Note that the most obvious reason to run over would be an enum containing a value that is not listed - which is undefined behaviour anyway as pointed out in the answer by #M.M.
This way, you get rid of the warning on current versions of both GCC and Clang without introducing new warnings.
What you lose is the protection by the compiler if you miss a valid situation that does run over the switch statement. This is mitigated to some degree that both GCC and Clang do warn you if miss a switch case completely (e.g. if a value gets added to the enum), but not if one of the cases runs into a break statement.

How can I know if C++ compiler evaluates the expression at compile time?

I have a code like this
const int Value = 123 * 2 + GetOffset();
GetOffset is a constexpr function returning int.
How can I make sure this expression is indeed evaluated at compile time?
Why don't you use constexpr for Value too? I think it will ask the compiler to evaluate it,
constexpr int Value = 123 * 2 + GetOffset();
if the function GetOffset() is simple and meet the requirements of constexpr.
The requirements are
the function must have a non-void return type.
the function body cannot declare variables or define new types.
the body may contain only declarations, null statements and a single return
statement.
Since Getoffset() returns int, it meets the first one.
You can't ensure the compiler does this. You generally need to enable optimization, including some level of function inlining. What these options are depend on your compiler and its version.
You can check the generated assembly to see if it contains a call to GetOffset or just uses a constant determined by the compiler.
what if you declare 'Value' as constexpr too? Actually you can probalby never be sure if something is evaluated at compilation time, however in this case there is no reason why it could not be evaluated.
One possibility is to use std::ratio. From section 20.10.1 of the C++11 standard:
This subclause describes the ratio library. It provides a class template ratio which exactly represents any finite rational number with a numerator and denominator representable by compile-time constants of type intmax_t.
So according to the standard, this would only be valid for a compile-time constant:
const int value = std::ratio<123 * 2 + GetOffset()>::num;
So this would guarantee that the expression is evaluated at compile time. However, it doesn't also guarantee that the expression is not evaluated at run time.
You can't be absolutely sure; the compiler is only required to generate code with the specified behaviour, and calculating it at compile- or run-time would not change the behaviour.
However, the compiler is required to be able to evaluate this at compile time, since it can be used where only compile-time constants are allowed such as array sizes and template arguments; so there's no reason why a sane compiler shouldn't perform that obvious optimisation. If the compiler doesn't (at least with optimisations enabled), throw it away and find a better one.
You can check the assembly produced by the compiler to see whether it calculates the value; but this in itself doesn't guarantee that future builds will do the same.
Considering that I have not used C++ in over half a decade now, the chances of the suggestion being way of the mark are quite high, but what about using inline for the function.
If the function returns a certain predefined value, available at the compile time, then the compiler should be able to make use of that value.
Create a separate source file with the expression. Evaluate printf("#define MyExpression %d.\n", expression);. When building your project, compile this source file for the native system and execute it. Include the resulting output as a header in your regular sources.
If you want to confirm that the initializer is a constant expression than you can use the constexpr specifier:
constexpr int Value = 123 * 2 + GetOffset();
It will fail to compile if it isn't a constant expression.
It is theoretically unspecified whether a constexpr variable Value is actually calculated during translation - but in practice you can be sure it is.
Just assert it: static_assert(Value == 123 * 2 + GetOffset(), "constexpr");
Doesn't get any simpler than that.

What does a "true;" or "10;" statement mean in C++ and how can it be used?

In C++ one can write any of the following statements:
10;
true;
someConstant; //if this is really an integer constant
or something like
int result = obtainResult();
result; // looks totally useless
The latter can be used to suppress a compiler warning "A variable is initialized but not referenced" (C4189 in VC++) if a macro that is expanded into an empty string in some configuration is later used with the result variable. Like this:
int result = obtainResult();
result;
assert( result > 0 ); // assert is often expanded into an empty string in Release versions of code
What's the meaning of such statements? How can they be used except for compiler warning suppression?
This kind of statements is a logical expansion of how other pieces of the language works. Consider having a function that returns a value, for example int foo(), that also has some side effects. Sometimes you only want those side effects to happen, so you write foo(); as a statement.
Now, while this does not look exactly like 10;, the function call will evaluate to an int sooner or later, and nothing happens to that int, just like with 10;.
Another example of the same issue is that since you can do a = b = 10;, that means b = 10 has to evaluate to 10, hence you can not do assignment without generating a value that has to be suppressed.
Being able to write such values as statements is just a logical way of building the language, but for the cases you present it might even be a good idea to give a compiler warning for it.
Unless you use it to suppress compiler warnings ;)
These statements (called expression-statements in the C++ grammar) are valid because they are expressions.
Expressions are all constructs that calculate some kind of value, such as
3 + 5
someVariable
someFunctionCall( 2 )
someVar += 62
val > 53
I think, to keep the grammar simple, they decided to not differentiate between those expressions that actually have a side effect (such as the function call or the assignment) and those that don't.
Such a statement does nothing, and will most likely be optimized away by any decent compiler.
It may be useful for getting rid of the unused variable warning, but with some compilers you may get a statement has no effect warning instead.
They have no practical use beyond compiler warning suppression, and in general the compiler will elide any such constant-value statement that has no side effect.
They are expressions that will be evaluated, assuming the compiler doesn't optimise them away. As for "meaning", I'm not sure what you "mean" by that!
In C and C++, a statement that is just an expression is evaluated.
The fact that the expression might be useless is harmless, and with the optimizer turned on can result in no code generated at all. However, as you've observed, it usually does count as use of a variable.
Note that statements containing only an expression are quite common. A simple function call is one, for example. In printf("hello, world.\n");, the return value of printf() is ignored but the function is still called and its intended effect (text output) happens.
Also, a variable assignment x = 3; is also a statement made up of a simple expression, since assignment is an operator and returns a value in addition to its side effect of modifying the lvalue.
Although legal I think these statements are confusing and should be avoided, even for suppressing warnings. For me it is more reasonable to suppress the warning using something like this:
int result = 0;
result = obtainResult();
assert (result > 0);
In some embedded enviroments accessing a read only register will have side effects, e.g. clearing it.
Writing int temp = IV; to clear it causes a warning because temp isn't used, in which case I write IV;
I agree with Magnus' answer. There is one thing that puzzles me though: why do you use this nonsense
int result = obtainResult();
result; // looks totally useless
to get rid of compiler warnings? In my humble opinion it is much worse NOT to have a warning in such situation. The result variable is still not used - you have just "swept the dirt under the carpet". This "lone variable" approach looks as if there was something missing (Have I accidently deleted something?). Why don't you use
(void)obtainResult();
in the first place? It assures anyone who would be reading your code that you do not care about the return result. It is very difficult to put this "accidently". Obviously this does not generate any compiler warnings.