Can code that will never be executed invoke undefined behavior?

CLanguage Lawyer

C Problem Overview


The code that invokes undefined behavior (in this example, division by zero) will never get executed, is the program still undefined behavior?

int main(void)
{
    int i;
    if(0)
    {
        i = 1/0;
    }
    return 0;
}

I think it still is undefined behavior, but I can't find any evidence in the standard to support or deny me.

So, any ideas?

C Solutions


Solution 1 - C

Let's look at how the C standard defines the terms "behavior" and "undefined behavior".

References are to the N1570 draft of the ISO C 2011 standard; I'm not aware of any relevant differences in any of the three published ISO C standards (1990, 1999, and 2011).

Section 3.4:

> behavior
> external appearance or action

Ok, that's a bit vague, but I'd argue that a given statement has no "appearance", and certainly no "action", unless it's actually executed.

Section 3.4.3:

> undefined behavior
> behavior, upon use of a nonportable or erroneous program construct or of erroneous data, > for which this International Standard imposes no requirements

It says "upon use" of such a construct. The word "use" is not defined by the standard, so we fall back to the common English meaning. A construct is not "used" if it's never executed.

There's a note under that definition:

> NOTE Possible undefined behavior ranges from ignoring the situation > completely with unpredictable results, to behaving during translation > or program execution in a documented manner characteristic of the > environment (with or without the issuance of a diagnostic message), to > terminating a translation or execution (with the issuance of a > diagnostic message).

So a compiler is permitted to reject your program at compile time if its behavior is undefined. But my interpretation of that is that it can do so only if it can prove that every execution of the program will encounter undefined behavior. Which implies, I think, that this:

if (rand() % 2 == 0) {
    i = i / 0;
}

which certainly can have undefined behavior, cannot be rejected at compile time.

As a practical matter, programs have to be able to perform runtime tests to guard against invoking undefined behavior, and the standard has to permit them to do so.

Your example was:

if (0) {
    i = 1/0;
}

which never executes the division by 0. A very common idiom is:

int x, y;
/* set values for x and y */
if (y != 0) {
    x = x / y;
}

The division certainly has undefined behavior if y == 0, but it's never executed if y == 0. The behavior is well defined, and for the same reason that your example is well defined: because the potential undefined behavior can never actually happen.

(Unless INT_MIN < -INT_MAX && x == INT_MIN && y == -1 (yes, integer division can overflow), but that's a separate issue.)

In a comment (since deleted), somebody pointed out that the compiler may evaluate constant expressions at compile time. Which is true, but not relevant in this case, because in the context of

i = 1/0;

1/0 is not a constant expression.

A constant-expression is a syntactic category that reduces to conditional-expression (which excludes assignments and comma expressions). The production constant-expression appears in the grammar only in contexts that actually require a constant expression, such as case labels. So if you write:

switch (...) {
    case 1/0:
    ...
}

then 1/0 is a constant expression -- and one that violates the constraint in 6.6p4: "Each constant expression shall evaluate to a constant that is in the range of representable values for its type.", so a diagnostic is required. But the right hand side of an assignment does not require a constant-expression, merely a conditional-expression, so the constraints on constant expressions don't apply. A compiler can evaluate any expression that it's able to at compile time, but only if the behavior is the same as if it were evaluated during execution (or, in the context of if (0), not evaluated during execution().

(Something that looks exactly like a constant-expression is not necessarily a constant-expression, just as, in x + y * z, the sequence x + y is not an additive-expression because of the context in which it appears.)

Which means the footnote in N1570 section 6.6 that I was going to cite:

> Thus, in the following initialization,
> static int i = 2 || 1 / 0;
> the expression is a valid integer constant expression with value one.

isn't actually relevant to this question.

Finally, there are a few things that are defined to cause undefined behavior that aren't about what happens during execution. Annex J, section 2 of the C standard (again, see the N1570 draft) lists things that cause undefined behavior, gathered from the rest of the standard. Some examples (I don't claim this is an exhaustive list) are:

> - A nonempty source file does not end in a new-line character which is not immediately preceded by a backslash character or ends in a partial > preprocessing token or comment > - Token concatenation produces a character sequence matching the syntax of a universal character name > - A character not in the basic source character set is encountered in a source file, except in an identifier, a character constant, a string > literal, a header name, a comment, or a preprocessing token that is > never converted to a token > - An identifier, comment, string literal, character constant, or header name contains an invalid multibyte character or does not begin > and end in the initial shift state > - The same identifier has both internal and external linkage in the same translation unit

These particular cases are things that a compiler could detect. I think their behavior is undefined because the committee didn't want to, or couldn't, impose the same behavior on all implementations, and defining a range of permitted behaviors just wasn't worth the effort. They don't really fall into the category of "code that will never be executed", but I mention them here for completeness.

Solution 2 - C

This article discusses this question in section 2.6:

int main(void){
      guard();
      5 / 0;
}

The authors consider that the program is defined when guard() does not terminate. They also find themselves distinguishing notions of “statically undefined” and “dynamically undefined”, e.g.:

> The intention behind the standard11 appears to be that, in general, situations are made statically undefined if it is not easy to generate code for them. Only when code can be generated, then the situation can be undefined dynamically. > > 11) Private correspondence with committee member.

I would recommend looking at the entire article. Taken together, it paints a consistent picture.

The fact that the authors of the article had to discuss the question with a committee member confirms that the standard is currently fuzzy on the answer to your question.

Solution 3 - C

In this case the undefined behavior is the result of executing the code. So if the code is not executed, there is no undefined behavior.

Non executed code could invoke undefined behavior if the undefined behavior was the result of solely the declaration of the code (e.g. if some case of variable shadowing was undefined).

Solution 4 - C

I'd go with the last paragraph of this answer: https://stackoverflow.com/a/18384176/694576

>... UB is a runtime issue, not a compiletime issue ...

So, no, there is no UB invoked.

Solution 5 - C

The standard says, as I remember right, it's allowed to do anything from the moment, a rule got broken. Maybe there are some special events with kind of global flavour (but I never heard or read about something like that)... So I would say: No this can't be UB, because as long the behavior is well defined 0 is allways false, so the rule can't get broken on runtime.

Solution 6 - C

Only when the standard makes breaking changes and your code suddenly is no longer "never gets executed". But I don't see any logical way in which this can cause 'undefined behaviour'. Its not causing anything.

Solution 7 - C

On the subject of undefined behaviour it is often hard to separate the formal aspects from the practical ones. This is the definition of undefined behaviour in the 1989 standard (I don't have a more recent version at hand, but I don't expect this to have changed substantially):

1 undefined behavior
behavior, upon use of a nonportable or erroneous program construct or of
erroneous data, for which this International Standard imposes no requirements
2 NOTE Possible undefined behavior ranges from ignoring the situation completely
with unpredictable results, to behaving during translation or program execution
in a documented manner characteristic of the environment (with or without the
issuance of a diagnostic message), to terminating a translation or
execution (with the issuance of a diagnostic message).

From a formal point of view I'd say your program does invoke undefined behaviour, which means that the standard places no requirement whatsoever on what it will do when run, just because it contains division by zero.

On the other hand, from a practical point of view I'd be surprised to find a compiler that didn't behave as you intuitively expect.

Solution 8 - C

>I think it still is undefined behavior, but I can't find any evidence in the standard to support or deny me.

I think the program does not invoke undefined behavior.

Defect Report #109 addresses a similar question and says:

> Furthermore, if every possible execution of a given program would result in undefined behavior, the given program is not strictly conforming. A conforming implementation must not fail to translate a strictly conforming program simply because some possible execution of that program would result in undefined behavior. Because foo might never be called, the example given must be successfully translated by a conforming implementation.

Solution 9 - C

It depends on how the expression "undefined behavior" is defined, and whether "undefined behavior" of a statement is the same as "undefined behavior" for a program.

This program looks like C, so a deeper analysis of what the C standard used by the compiler (as some answers did) is appropriate.

In absence of a specified standard, the correct answer is "it depends". In some languages, compilers after the first error try to guess what the programmer might mean and still generate some code, according to the compilers guess. In other, more pure languages, once somerthing is undefined, the undefinedness propagate to the whole program.

Other languages have a concept of "bounded errors". For some limited kinds of errors, these languages define how much damage an error can produce. In particular languages with implied garbage collection frequently make a difference whether an error invalidates the typing system or does not.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionYu HaoView Question on Stackoverflow
Solution 1 - CKeith ThompsonView Answer on Stackoverflow
Solution 2 - CPascal CuoqView Answer on Stackoverflow
Solution 3 - CArnaud Le BlancView Answer on Stackoverflow
Solution 4 - CalkView Answer on Stackoverflow
Solution 5 - CdheinView Answer on Stackoverflow
Solution 6 - CHrishiView Answer on Stackoverflow
Solution 7 - CNicola MusattiView Answer on Stackoverflow
Solution 8 - CouahView Answer on Stackoverflow
Solution 9 - Cuser3138160View Answer on Stackoverflow