MISRA Discussion Forums

Full Version: Rule 8.10, I don't understand the Rationale
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
The Rationale for rule 8.10 says that inline functions with external linkage are not to be used, because it is not defined whether the function will in fact be inlined, or the external definition will be called.

However, this is also not defined for static functions: "Making a function an inline function suggests that calls to the function be as fast as possible. The extent to which such suggestions are effective is implementation-defined." (I'm using a draft of the C99 standard, http://www.open-std.org/JTC1/sc22/wg14/w.../n1256.pdf and this is 6.7.4.5. Footnote 121 spells it out more clearly: "For example, an implementation might never perform inline substitution...".)

So the behaviour of static inline functions is undefined in the same way as inline functions with external linkage.

It has been suggested that inline functions defined in header files should be defined with static linkage. Paragraph 6.7.4.3 of the above pdf says external linkage cannot reference indentifiers with internal linkage, but paragraph 6.7.4.6 says that any function with internal linkage can be inlined. So if an inline function is declared static, it can have local static variables. But there will be a different instance of the inline function for each .c file which includes the header with the function's definition, and each would have its own static variables! Effectively, you have a function definition with a static variable, the value of which differs depending on where it is called from. This will surely be unexpected behaviour to some programmers, and is therefore dangerous.

Was this considered when specifying that all inline functions should be static? Or is the draft wildly different from the final C99 spec?
I think that the undefined behaviour in question arises from this part of N1256, Section 6.7.4:

Quote:For a function with external linkage, the following restrictions apply: If a function is declared with an inline function specifier, then it shall also be defined in the same translation unit.

The undefined behaviour (caused by violation of a "shall" or "shall not" - see N1256, Section J.2, first bullet) can only arise if a function with external linkage, declared with the inline specifier, isn't defined in the same translation unit. So, this can't apply to functions with internal linkage.

The standard is explicit in stating that each copy of an inline function has its own static variables (N1256, Section 6.7.4, footnote 122) so I agree that there's certainly scope for unexpected behaviour as you suggest. Within a given translation unit, this could be avoided by declaring static objects at file scope, although this might violate Rule 8.9 (advisory). However, it wouldn't help when there are copies of the inline function in multiple translation units.

Therefore, I think that the rule is aimed at avoiding the undefined behaviour only and doesn't address the (possibly) unexpected behaviour. Avoiding storing state in inline functions would seem to be a good idea nonetheless.

So far as I am aware, there are no significant differences between N1256 and the published standard (as corrected).
The footnote you mention, 122, seems to reinforce my impression that having static inline function definitions in a header is a very dangerous coding practice! So why is MISRA promoting that sort of coding?

The rationale mentions two scenarios, and making the inline function static is not a good solution to either one.

Your quote from section 6.7.4 paragraph 6 relates to the first scenario: declaring the function but not defining it. (If the definition is not in the translation unit, it shouldn't even compile, because of that "shall". But I suppose compilers do let you get away with stuff...)

But declaring the function static is not a good solution to that - the rule could just insist on the definition being provided! That's how extern inline functions are supposed to work, put the definition in the header, so the function can be inlined if the compiler thinks it can/should do so; put a declaration in one .c file to tell the compiler where to put the actual instance of the function, so that any translation unit in which the compiler didn't feel up to doing the inlining, has something to call.

Using extern means that the function cannot contain a modifiable object with static storage duration or refer to anything that has internal linkage (6.7.4 par 3) so it's safe to put the definition in the header file. But it's not safe to put the definition of an inline function with static storage duration in a header file, because if it's not extern, it can reference such things!

The rationale then goes on to refer to the fact that it is undefined whether the inline definition or a function call will be used. But making the function static is not a solution to that either! Even if the inline function is static, the inline is only a suggestion; the extent to which such suggestions are effective is implementation-defined (6.7.4 par 6 and footnote 121). So, even if it is static, the function may be inlined, or called; it is even possible that it could be inlined when the function is used in one place in the translation unit, and called in another, depending on context.

So I'm getting more and more convinced that rule 8.10 is just wrong. It will not make code safer, and could introduce bugs. Someone please convince me otherwise?
When "shall" or "shall not" appears in a Constraint section of the standard, then the compiler must issue some kind of message if the "shall" or "shall not" is violated. However, when "shall" or "shall not" appear anywhere else in the standard, violation results in undefined behaviour (see N1256, Section 4, paragraph 2). The nature of the undefined behaviour might well be a refusal to compile the program, but it might be to do something else. Its the possibility of this "something else" which the rule is aiming to prevent.

I'll defer to someone who represents the MISRA C Working Group to convince you, or otherwise, of the merits of the rule.
Ok, from the wording of the rule - but not the rationale - that may be the undefined behaviour that rule 8.10 was intended to prevent.

The rationale appears to disagree, though. It describes behaviour which is equally undefined for inline functions of static, as well as external, linkage.

But this undefined behaviour is avoided by the normal (for inline functions) practice of defining the external function in the header file, and having the declaration in the single translation unit which is to contain the external instance of the function. This is suggested by the Note at the end of the rationale; it should have been in the text of the rule. From N1256 paragraph 6.7.4.3 and footnote 122 etc., it seems that coding guidelines concerned with code safety should also forbid static inline functions shared among several translation units, instead of allowing them and even promoting their use.

It would be nice if someone from Misra could explain why this rule, and in particular the definition of static inline functions in headers, is allowable in code that wants to ensure correct behaviour.
Rule 8.10 was designed to cover the unspecified and undefined behaviour concerning inline functions.

The use of static inline functions does not exhibit unspecified or undefined behaviour (see note) and is therefore permitted by the MISRA-C guidelines. The declaration and use of static objects within a static inline function also does not exhibit unspecified or undefined behaviours. However, the MISRA-C working group agrees that such use of static objects might give rise to behaviours that is not expected by the coder. This issue will be considered in a future release of MISRA-C.

Note: The working group agrees with the post by Steve Montgomery which explains that the undefined behaviour in section 6.7.4.5 applies only to inline functions with external linkage, and therefore not to static inline functions.
Thanks, that does help me understand what is going on, and make me comfortable in getting a deviation from this rule for my code.

May I assume the Note refers to the first post from Steve Montgomery in this thread? He quotes 6.7.4.6, not 6.7.4.5 as implied by the Note. His quote says that an inline function with external linkage is to be defined in the same translation unit. And, yes, a function with internal linkage cannot violate the clause he quotes, because you cannot use such a function without having the definition in the translation unit. But the problem also does not arise if the function definition is in the .h file, with one extern declaration in one .c file which also #includes the header, to indicate the location of the external definition. In this case too, the definition is in the translation unit containing the declaration.

(6.7.4.6 also implies that there could be different internal and external definitions for an inline function with external linkage. I hope that is not what we are talking about. Having the definition in the .h file will avoid that.)

But the implementation-defined behaviour of section 6.5.4.5 remains. The function may be expanded inline by the compiler, or there may be an instance of the function, which is called - regardless of whether the function has internal or external linkage. This is true at least of both of the compilers I use (IAR, GCC), so I have to live with it even if your reading of the standard suggests it should be otherwise for static functions. (Are there any readily-available compilers that always inline all static inline functions, regardless of optimisation settings or function complexity?)

Now, as you say, it is true that putting static inline functions in header files does not exhibit unspecified or undefined behaviours. Neither does using a simple statement with no braces as the body of an 'if', but I wouldn't do that either. Putting static inline functions in a header can lead - and for me, has led - to a bug, which is why I came here in the first place. So a deviation looks like my best option.