IssueInline constraint not respected occurs when you refer
to a file scope modifiable static variable or define a local modifiable static variable
in a nonstatic inlined function. The checker considers a variable as modifiable if it is
not const-qualified.
For instance, var is a
modifiable static variable defined in an
inline function func.
g_step is a file scope modifiable static variable
referred to in the same inlined
function.
static int g_step;
inline void func (void) {
static int var = 0;
var += g_step;
} RiskWhen you modify a static variable in
multiple function calls, you expect to modify the same variable in each call.
For instance, each time you call func, the same instance of
var1 is incremented but a separate instance of
var2 is
incremented.
void func(void) {
static var1 = 0;
var2 = 0;
var1++;
var2++;
}If a function has an inlined and non-inlined definition (in separate files), when
you call the function, the C standard allows compilers to use either the inlined or
the non-inlined form (see ISO®/IEC 9899:2011, sec. 6.7.4). If your compiler uses an inlined
definition in one call and the non-inlined definition in another, you are no longer
modifying the same variable in both calls. This behavior defies the expectations
from a static variable.
FixUse one of these fixes:
If you do not intend to modify the variable, declare it as
const.
If you do not modify the variable, there is no question of unexpected
modification.
Make the variable non-static. Remove the
static qualifier from the declaration.
If the variable is defined in the function, it becomes a regular local
variable. If defined at file scope, it becomes an extern variable. Make
sure that this change in behavior is what you intend.
Make the function static. Add a
static qualifier to the function
definition.
If you make the function static, the file with the
inlined definition always uses the inlined definition when the function
is called. Other files use another definition of the function. The
question of which function definition gets used is not left to the
compiler.
Example - Static Variable Use in Inlined and External Definition/* file1. c : contains inline definition of get_random()*/
inline unsigned int get_random(void)
{
static unsigned int m_z = 0xdeadbeef;
static unsigned int m_w = 0xbaddecaf;
/* Compute next pseudorandom value and update seeds */
m_z = 36969 * (m_z & 65535) + (m_z >> 16);
m_w = 18000 * (m_w & 65535) + (m_w >> 16);
return (m_z << 16) + m_w;
}
int call_get_random(void)
{
unsigned int rand_no;
int ii;
for (ii = 0; ii < 100; ii++) {
rand_no = get_random();
}
rand_no = get_random();
return 0;
}/* file2. c : contains external definition of get_random()*/
extern unsigned int get_random(void)
{
/* Initialize seeds */
static unsigned int m_z = 0xdeadbeef;
static unsigned int m_w = 0xbaddecaf;
/* Compute next pseudorandom value and update seeds */
m_z = 36969 * (m_z & 65535) + (m_z >> 16);
m_w = 18000 * (m_w & 65535) + (m_w >> 16);
return (m_z << 16) + m_w;
}In this example, get_random() has an inline definition in
file1.c and an external definition in
file2.c. When get_random is called in
file1.c, compilers are free to choose whether to use the
inline or the external definition.
Depending on the definition used, you might or might not modify the version of
m_z and m_w in the inlined version of
get_random(). This behavior contradicts the usual
expectations from a static variable. When you call get_random(),
you expect to always modify the same m_z and
m_w.
Correction — Make Inlined Function StaticOne possible correction is to make the inlined get_random()
static. Irrespective of your compiler, calls to get_random()
in file1.c then use the inlined definition. Calls to
get_random() in other files use the external definition.
This fix removes the ambiguity about which definition is used and whether the
static variables in that definition are modified.
/* file1. c : contains inline definition of get_random()*/
static inline unsigned int get_random(void)
{
static unsigned int m_z = 0xdeadbeef;
static unsigned int m_w = 0xbaddecaf;
/* Compute next pseudorandom value and update seeds */
m_z = 36969 * (m_z & 65535) + (m_z >> 16);
m_w = 18000 * (m_w & 65535) + (m_w >> 16);
return (m_z << 16) + m_w;
}
int call_get_random(void)
{
unsigned int rand_no;
int ii;
for (ii = 0; ii < 100; ii++) {
rand_no = get_random();
}
rand_no = get_random();
return 0;
}/* file2. c : contains external definition of get_random()*/
extern unsigned int get_random(void)
{
/* Initialize seeds */
static unsigned int m_z = 0xdeadbeef;
static unsigned int m_w = 0xbaddecaf;
/* Compute next pseudorandom value and update seeds */
m_z = 36969 * (m_z & 65535) + (m_z >> 16);
m_w = 18000 * (m_w & 65535) + (m_w >> 16);
return (m_z << 16) + m_w;
}