V2667. MISRA. The value of an expression and its persistent side effects must be the same under all permitted evaluation orders and must be independent from thread interleaving.
This diagnostic rule is based on the MISRA (Motor Industry Software Reliability Association) software development guidelines.
This diagnostic rule is relevant only for C and C++ until the C++11 standard.
The analyzer has detected unordered volatile operations on the same object. The behavior of such operations is undefined, and they may lead to unexpected program behavior.
The C and C++ standards allows compiler to freely set the order of evaluation to achieve code optimization. As a result, the intended sequence of operations should be defined manually.
To avoid unexpected results or undefined behavior between two adjacent sequence points, the MISRA standard prohibits:
- modify an object more than once;
- modify and read an object, unless the read contributes to the evaluation of the object value;
- modify more than one
volatileor atomic object; - read more than one
volatileobject or the samevolatileobject more than once; - read the same atomic object more than once.
Note N1. The volatile object refers to a variable declared with the volatile qualifier or a structure data member declared with this qualifier.
The example:
volatile int a;
const volatile char b = 5;
volatile int* pointer;
_Atomic volatile int av;
struct TestStruct
{
int x;
};
volatile TestStruct ts;
Both ts and ts.x are volatile-objects.
The atomic object is a variable declared with the _Atomic(....) specifier or the _Atomic qualifier, or the structure data member declared with such a specifier or qualifier.
The example:
_Atomic int a;
_Atomic const float f = 1.0;
_Atomic(char) c;
atomic_long l;
_Atomic volatile int av;
struct TestStruct
{
int x;
};
_Atomic TestStruct ts;
In this example, both ts and ts.x are atomicobjects.
Note N2. In imperative programming, a sequence point is a point in a program where it is guaranteed that all side effects of previous evaluations have already completed, and the side effects of subsequent computations have not completed yet. In the C language, sequence points are described in Annex C of the standard.
Note N3. All expression subobjects are handled for determining read/write regardless of any known values.
The example:
int n = 1;
int m = n || n++ + n;
Even though the n++ + n expression is never executed in this context, the analyzer will still issue a warning.
Look at examples for each rule requirement.
The example N1. It is prohibited to modify an object more than once between adjacent sequence points:
int n, m;
n = n++;
n = ++n + n;
n = (n = 4);
m = n++ + ++n;
foo(n++, n++);
In each line, n is modified twice, which violates the requirement N1.
The fixed code:
n = m++;
n = m + m * 200;
n = 4 + (m = 2);
n = m = n + m;
foo(n++, m++);
The example N2. It is prohibited to modify or read an object unless the read contributes to the evaluation of the object value:
int n, m;
int m = n + (n = 2);
m = n++ + n;
m = foo(n, n++);
In each line, n is modified and read, which violates requirement N2.
The fixed code:
n = n + 2;
n++;
m = n++;
Although n is modified and read in each line, the n variable is only read to evaluate the new n value.
The example N3. It is prohibited to modify more than one volatile or atomic object.
volatile int volat1, volat2;
_Atomic int atom1, atom2;
int n = atom1++ + atom2++; // <= (A2)
n = (atom1 = 0) + (atom2 = 1); // <= (A2)
n = atom1++ + volat1++; // <= (A1V1)
n = (atom1 = 0) + (volat1 = 1); // <= (A1V1)
n = volat1++ + volat2++; // <= (V2)
n = (volat1 = 0) + (volat2 = 1); // <= (V2)
This example demonstrates:
- in the lines marked
A2, two atomic objects are modified; - in the lines marked
A1V1, one atomic and onevolatileobject are modified; - in the lines marked
V2, twovolatileobjects are modified.
All of these cases violate requirement N3.
The fixed code:
volat1++;
n = atom1++;
n = (volat1 = 0) + atom1;
n = (volat1 = 0, volat2 = 1); // <= (SP)
m = (volat1 = n) || (volat2 = n + 1); // <= (SP)
In each line marked SP, modifications occur out of adjacent sequence points.
The example N4. It is prohibited to read more than one volatile object or the same volatile object more than once:
volatile int volat1, volat2;
_Atomic int atom1, atom2;
int n = volat1 + volat1; // <= (V1)
n = volat1 + volat2; // <= (V2)
foo(volat1, volat1); // <= (V1)
foo(volat1, volat2); // <= (V2)
In this example, volatile objects are read during addition operations and when passing arguments by copy to a function call.
This example demonstrates:
- in the lines marked
V1, the samevolatileobject is read more than once; - in the lines marked
V2, twovolatileobjects are read.
All of these cases violate requirement N4.
The fixed code:
n = volat1;
volat1 = volat2;
volat1 = volat1 + 42;
foo(volat1, n);
The example N5. It is prohibited to read the same atomic object more than once.
_Atomic int atom1, atom2;
int n = atom1 + atom1;
foo(atom1, atom1);
n = foo(atom1, 0) + atom1;
In this example, atomic objects are read during addition operations and when arguments are passed by value to a function call. All of these cases violate the rule requirement N5.
The fixed code:
n = atom1 + atom2;
atom1 = atom1 + 42;
n = atom1 && atom1; // <= (SP)
n = foo(atom1, 0), atom1; // <= (SP)
In each line marked SP, reading occurs out of adjacent sequence points.
This diagnostic is classified as:
|