c++language-lawyer

What does "execution contains an undefined operation" actually mean?


C++ (n4917.pdf), 4.1.2p5 (emphasis added):

A conforming implementation executing a well-formed program shall produce the same observable behavior as one of the possible executions of the corresponding instance of the abstract machine with the same program and the same input. However, if any such execution contains an undefined operation, this document places no requirement on the implementation executing that program with that input (not even with regard to operations preceding the first undefined operation).

What does "execution contains an undefined operation" actually mean?

Is it about a presence (i.e. w/o an execution) of an undefined operation, or is it about an execution of an undefined operation?


Solution

  • The abstract machine is parametrized by aspects that are stated to be implementation-defined in the standard, see [intro.abstract]/2.

    Each realization of implementation-defined behavior realizes one instance of the abstract machine.

    Each such instance of the abstract machine can be run for any program with any given input and is non-deterministic. Its non-deterministic aspects are behavior that the standard states to be unspecified. Each realization of all unspecified behaviors then determines one deterministic execution on the instance of the abstract machine for the given program and input, which are the operations performed by the abstract machine according to the program execution rules specified in the rest of the standard given the realizations of implementation-defined and unspecified behavior for that program and input. See [intro.abstract]/3.

    Your quote states that, given the C++ program and any particular realization of program inputs, if any execution (i.e. realization of unspecified behaviors) would result in executing an operation that has undefined behavior, then the compiler does not have to guarantee any particular behavior of the program for that input.

    Otherwise the compiler has to assure that the observable behavior of the program with the given input corresponds to the observable behaviors that at least one execution (i.e. realization of unspecified behaviors) would have on the instance of the abstract machine.

    All of this is talking about executions on the abstract machine, not actual execution of the compiled code. The point of view is external, also to the time dimension. When determining whether the compiler needs to guarantee any particular behavior, we look at all inputs to the program for the whole execution. If the program would have undefined behavior in one realization of unspecified behavior for a given such set of inputs over the whole execution time, then there are no requirements on observable behavior at all, not even at the start of the program execution, if these inputs will occur.

    Of course, in practice user inputs are not deterministic and we can't predict future inputs, so as long as inputs that would not cause undefined behavior are possible, we have to make sure that the program can produce valid observable behavior for such inputs.

    But if we can determine at some point that the program execution will for any possible future set of inputs have undefined behavior for one realization of unspecified behaviors per such input realization, then there is no requirement to produce any particular observable behavior anymore, even if naively following the ongoing execution according to the rules of the abstract machine would produce further observable behavior before the undefined operation or the user input leading to the undefined operation occurs.