Main Content

Inline constraint not respected

Non-const static variable is modified in nonstatic inline function

Description

This defect occurs when you refer to a file scope non-const static variable or define a local non-const static variable in a nonstatic inlined function.

For instance, var is a non-const static variable defined in an inline function func. g_step is a file scope non-const static variable referred to in the same inlined function.

static int g_step;
inline void func (void) {
   static int var = 0;  // Defect
   var += g_step; // Defect
}

Risk

Modifying non-const static variable in a nonstatic inline function can result in unexpected and incorrect result. Consider an extern function func() that you want to use as an inlined function in file2.cpp. A non-inline version of the function is provided in file1.cpp so that there is an external definition of the function.

// file1.c
void func(void) {
   static var1 = 0;
   var2 = 0;
   var1++;
   var2++;
}
// file2.c
extern inline void func(void) {
   static var1 = 0;
   var2 = 0;
   var1++;
   var2++;
}

If func() modified a non-const static variable, the behavior of the function is unpredictable. If you call func(), the compiler is allowed to invoke either the inlined version or the non inlined version. See ISO®/IEC 9899:2011, sec. 6.7.4. It is possible that different calls to func() does not modify the same static variable var1. This behavior is unexpected can lead to incorrect results.

Fix

Use 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 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.

Examples

expand all

/* 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 by the compiler, 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. Because the static local variables in all function definitions are shared across all translation units in C++, this issue is not relevant if you compile this code as C++ code.

Correction — Make Inlined Function Static

One 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;
}

Result Information

Group: Programming
Language: C | C++
Default: On for handwritten code, off for generated code
Command-Line Syntax: INLINE_CONSTRAINT_NOT_RESPECTED
Impact: Medium

Version History

Introduced in R2018a

expand all