C++ Logo

std-discussion

Advanced search

Re: Is this program undefined-behavior or implementation-defined behavior?

From: Jennifier Burnett <jenni_at_[hidden]>
Date: Fri, 09 Oct 2026 16:23:41 +0100
[intro.race] p17 says that. If volatile variables on a particular implementation are *not* defined to be atomic it applies.

This is what I'm saying with the analogy to integer overflow, I'll give you an example. Consider the following program:
```
int main()
{
    int i = 2147483647;
    ++i;
    return 0;
}
```
Does this program have undefined behaviour? With a 32 bit int typical of modern systems, the answer would be yes, because I've deliberately caused the maximum 31 bit value to overflow, and integer overflow is undefined behaviour.

However, I might decide to run this program on a 36 bit system, in which case it's completely fine, or I might compile it on GCC with `-fwrapv`, in which case integer overflow becomes defined behaviour.

The original statement that the program has undefined behaviour doesn't become incorrect just because I can make up a situation in which it doesn't cause undefined behaviour. The point is that you cannot, in general, run that program on any C++ compiler and expect it to work. The program _only_ works on specific implementations that meet certain criteria, on every other possible implementation the behaviour will be undefined.

So, in your example your program *only* has defined behaviour when volatile variables are defined by the implementation to have atomic semantics. On any other possible implementation it will have undefined behaviour. Thus, it is, in general, undefined. The fact that we can invent a hypothetical implementation for which it would be well defined does not make that untrue.

To put it as simply as possible:
If the implementation says that volatile variables have atomic semantics your program does not have undefined behaviour. If the implementation does anything else then [intro.race] p17 applies and you have undefined behaviour. When people say this program has undefined behaviour they are simply alerting you to the fact that the second possibility exists.

On 9 October 2026 10:51:29 BST, jim x <xmh970252187_at_[hidden]> wrote:
>"integer overflow" is undefined behavior because the standard states that.
>I ask which rule in the standard says that the program contains a data
>race? Do you agree that the relevant rule is [intro.race] p17? If you
>agree, then the crux is: why is at least one operation of `#1` and `#2` not
>atomic?
>
>On Fri, Oct 9, 2026 at 5:46 PM Jennifier Burnett <jenni_at_[hidden]>
>wrote:
>
>> Because the standard doesn't define it and an implementation isn't
>> required to, nor is it required to ensure that any program that runs it has
>> any sane behaviour.
>>
>> When we're saying undefined we don't mean "undefined on all
>> implementations", that's impossible to claim because it's trivial to create
>> an implementation that defines any given piece of behaviour - GCC has a
>> command line option to make integer overflow defined, for example, but that
>> doesn't mean integer overflow isn't undefined. When people say a program
>> has undefined behaviour really what they mean is that it is that unless the
>> implementation goes out of it's way to make it defined (like with your
>> volatiles or GCC's integers), it'll be undefined.
>>
>>
>> On 9 October 2026 10:22:28 BST, jim x <xmh970252187_at_[hidden]> wrote:
>>
>>> The crux is: why do you think the program is undefined behavior without
>>> knowing the semantics of the access that would be specified by the concrete
>>> implementation? What's your reason for that?
>>>
>>> On Fri, Oct 9, 2026 at 5:03 PM Jennifier Burnett <jenni_at_[hidden]>
>>> wrote:
>>>
>>>> The crux of what I'm saying is that just because something is said to be
>>>> undefined in the standard doesn't make it undefined on a particular
>>>> implementation. In this hypothetical implementation accesses to volatile
>>>> variables are documented to be atomic, so [intro.race] p17 does not apply
>>>> and thus the behaviour is well-defined. A behaviour being defined by an
>>>> implementation is different from something being implementation-defined, if
>>>> that helps.
>>>>
>>>>
>>>> On 9 October 2026 08:55:52 BST, jim x <xmh970252187_at_[hidden]> wrote:
>>>>
>>>>> So, what behavior of the program do you think? If you think the
>>>>> program should be undefined behavior because the program contains a data
>>>>> race, let's step-by-step to go through the rules in [intro.race] p17. It's
>>>>> true that `#1` and `#2` are two potentially concurrent conflicting actions,
>>>>> and neither happens before the other. The crux is whether they satisfy the
>>>>> condition: at least one of which is not atomic. If one of them is
>>>>> non-atomic, then the condition is satisfied; therefore, the program
>>>>> contains a data race. If they are both atomic operations, the condition is
>>>>> false; no data race is contained. As I commented above, since the standard
>>>>> lets implementations define the semantics of the access, and a
>>>>> hypothetical implementation can define `#1` as `
>>>>> std::atomic_ref<int>(value).load()` and `#2` as `
>>>>> std::atomic_ref<int>(value).store(1)`; i.e., they're atomic
>>>>> operations, I don't think the crux condition in [intro.race] p17 is true.
>>>>> So, the program under this specified semantics shouldn't be UB. How do you
>>>>> explain this point?
>>>>>
>>>>> On Fri, Oct 9, 2026 at 3:39 PM David Brown <david.brown_at_[hidden]>
>>>>> wrote:
>>>>>
>>>>>>
>>>>>>
>>>>>> On 09/10/2026 09:16, jim x via Std-Discussion wrote:
>>>>>> > If the implementation defines that accesses to volatile variables
>>>>>> > are atomic, then they are atomic.
>>>>>> >
>>>>>> >
>>>>>> > In this case, does the program contain a data race as per
>>>>>> [intro.race]
>>>>>> > p17? If not, since the behavior depends on how the implementation
>>>>>> define
>>>>>> > the semantics, does that mean the behavior of the program should be
>>>>>> > implementation-defined?
>>>>>> >
>>>>>>
>>>>>> "Implementation-defined" in the standards does not just mean that the
>>>>>> implementation has to treat the construct in some particular way. It
>>>>>> means that the standard gives a range of options of how the
>>>>>> implementation should handle the construct, and that the
>>>>>> implementation
>>>>>> must document how it handles them.
>>>>>>
>>>>>> If an implementation's manual says that volatile accesses are atomic
>>>>>> (not unreasonable on single-core non-superscaler targets like most
>>>>>> microcontrollers), then the programmer can rely on the volatile
>>>>>> accesses
>>>>>> being atomic. But that does not mean unsynchronised volatile accesses
>>>>>> to the same address in different threads is implementation-defined
>>>>>> behaviour, or should be called such in the C++ standards.
>>>>>>
>>>>>>
>>>>>>

Received on 2026-10-09 15:23:57