26

Most people are familiar with the "undefined" and "unspecified" behaviour notes in C++, but what about "no diagnostic required"?

I note this question and answer, dealing with ill formed programs, but not much detail on the root of "no diagnostic required" statements.

What is the general approach applied by the committee when classifying something as "no diagnostic required"?

  • How bad does the error need to be for the standards committee to specify it as such?
  • Are these errors of such a nature that it would be near impossible to detect, hence diagnose?

Examples of "undefined" and "unspecified" behaviour are not in short supply; short of the ODR, what practical example(s) are there for the "no diagnostic required" type errors?

Community
  • 1
  • 1
Niall
  • 30,036
  • 10
  • 99
  • 142

2 Answers2

18

There was a discussion here: https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/lk1qAvCiviY with utterances by various committee members.

The general consensus appears to be

  • there is no normative difference
  • ill-formed; no diagnostic required is used only for compile-time rule violations, never for runtime rule violations.

As I said in that thread, I did once hear in a discussion (I can't remember anymore in which one, but I'm certain there were insightful committee members involved)

  • ill-formed; no diagnostic required for cases that clearly are bad rule violations and that can in principle be diagnosed at compile time, but would require huge efforts from an implementation.
  • undefined behavior for things that implementations could find useful meanings for, so don't neccessarily are pure evil, and for any runtime violations that results in arbitrary consequences.

The rough guide for me is; if it is at compile time, it tends to be "ill-formed; no diagnostic required" and if it is at runtime, it always is "undefined behavior".

Niall
  • 30,036
  • 10
  • 99
  • 142
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • That's an interesting discussion, so you know if they ever added those non normative notes? – Niall Jul 22 '14 at 18:38
  • @Niall I don't know. I can't find any trace of it in the issues list and new drafts. You may want to start a question on std-discussion if you want to go with an answer on that question. – Johannes Schaub - litb Jul 22 '14 at 18:53
  • 1
    Your suggested meaning coincides with what I would infer. Since the standard allows that even source code which is rejected by the compiler might generate a runnable executable, the fact that a runnable executable is produced does not mean the compiler endorsed the program as "well-formed". I would interpret "ill-formed; NDR" as saying that feeding ill-formed code to a compiler may yield a runnable program without issuing a diagnostic, but neither the compiler nor its documentation may legitimately declare that the program is "well-formed". – supercat Dec 12 '14 at 18:37
14

I would try to explain "no diagnostic required" for behaviours categorized as undefined behaviour (UB).

The Standard by saying "UB doesn't require diagnostic"1, gives compilers total freedom to optimize the code, as the compiler can eliminate many overheads only by assuming your program is completely well-defined (which means your program doesn't have UBs) which is a good assumption — after all if that assumption is wrong, then anything which compiler does based on this (wrong) assumption is going to behave in an undefined (i.e unpredictable) way which is completely consistent because your program has undefined behaviours anyway!

Note that a program which contains UBs has the freedom to behave like anything. Note again that I said "consistent" because it is consistent with Standard's stance : "neither the language specification nor the compilers give any guarantee of your program's behaviour if it contains UB(s)".

1. The opposite is "diagnostic required" which means the compiler is required to provide diagnostics to the programmer either by emitting warning or error messages. In other words, it is not allowed to assume the program is well-defined so as to optimize certain parts of code.

Here is an article (on LLVM blog) which explains this further using example:

An except from the article (italicised mine):

Signed integer overflow: If arithmetic on an 'int' type (for example) overflows, the result is undefined. One example is that "INT_MAX+1" is not guaranteed to be INT_MIN. This behavior enables certain classes of optimizations that are important for some code. For example, knowing that INT_MAX+1 is undefined allows optimizing "X+1 > X" to "true". Knowing the multiplication "cannot" overflow (because doing so would be undefined) allows optimizing "X*2/2" to "X". While these may seem trivial, these sorts of things are commonly exposed by inlining and macro expansion. A more important optimization that this allows is for "<=" loops like this:

for (i = 0; i <= N; ++i) { ... }

In this loop, the compiler can assume that the loop will iterate exactly N+1 times if "i" is undefined on overflow, which allows a broad range of loop optimizations to kick in. On the other hand, if the variable is defined to wrap around on overflow, then the compiler must assume that the loop is possibly infinite (which happens if N is INT_MAX) - which then disables these important loop optimizations. This particularly affects 64-bit platforms since so much code uses "int" as induction variables.

It is worth noting that unsigned overflow is guaranteed to be defined as 2's complement (wrapping) overflow, so you can always use them. The cost to making signed integer overflow defined is that these sorts of optimizations are simply lost (for example, a common symptom is a ton of sign extensions inside of loops on 64-bit targets). Both Clang and GCC accept the "-fwrapv" flag which forces the compiler to treat signed integer overflow as defined (other than divide of INT_MIN by -1).

I would recommend you to read the entire article — it has three parts, all are good.

Hope that helps.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • I did not know about the `-fcatch-undefined-behavior`. It's a long read, but I like how the clang is building useful tools to diagnose these things. – Niall Jul 22 '14 at 08:29
  • 5
    @Niall: Actually, that might well be obsolete now. Folks from Google have integrated UBSan in Clang now (Undefined Behavior Sanitizer) in the line of ASan (Address Sanitizer), MSan (Memory Sanitizer) and TSan (Thread Sanitizer). Each sanitizer inserts runtime checks for what is otherwise assumed at compile-time (they cannot necessarily be mixed together, you might need different build to exercise them all). Some (all ?) of those checks have also been ported to gcc by the way. The new option should be `-fsanitize=...` with `...` equal to `undefined` for the full UBSan. – Matthieu M. Jul 22 '14 at 08:35
  • 1
    It's too bad that there hasn't been any effort to categorize and allow specification of overflow behaviors other than "let anything happen" or "strictly wrap", since overflow-avoidance code is expensive and a lot of applications could meet requirements if there were some constraints on overflow behavior. For example, if overflow were specified as setting "errno", a lot of code which needs to report any overflows *which might lead to numerically-incorrect results* could be greatly simplified, and could be much better optimized than is presently possible, since user code which... – supercat Nov 26 '15 at 19:50
  • ...would set a flag if an overflow would occur in a calculation cannot be optimized away *even when the result of the calculation is otherwise unused*, but if the a rule said that compilers may, but are not required to, report overflows which cannot otherwise affect a program's output could have a much lower performance impact. – supercat Nov 26 '15 at 19:51