2

Let's say there is a C header file:

//void test(void);

inline void test(void) {}

If we comment out the first line, MISRA C checker will complain

Type: MISRA C-2012 Declarations and Definitions (MISRA C-2012 Rule 8.4, Required) Triage unavailable. header.h:3:

  1. misra_c_2012_rule_8_4_violation: Function definition does not have a visible prototype.

I don't understand why inline functions also need prototypes. Thank you.

  • 8
    The short, and probably unsatisfying, answer is because MISRA says so. – Retired Ninja Aug 15 '23 at 05:10
  • AFAIK, MISRA provides explanations to each of the rules. Probably, you define a function with external linkage, so MISRA wants you to provide a prototype. BTW, did you read the respective chapter in the applicable C standard? – the busybee Aug 15 '23 at 06:21
  • @thebusybee No, but I read https://en.cppreference.com/w/c/language/inline , it has: "If a non-static function is declared inline, then it must be defined in the same translation unit. The inline definition that does not use extern is not externally visible and does not prevent other translation units from defining the same function. This makes the inline keyword an alternative to static for defining functions inside header files, which may be included in multiple translation units of the same program. " So I assume it is legal after C99. – Home of the Brave Aug 15 '23 at 09:01
  • 2
    The problem with such resources is that they sometimes subtly change meanings. However, I'm not a language lawyer. :-D The standard (c2x draft) I'm now looking at says: "If all of the file scope declarations for a function in a translation unit include the `inline` function specifier without `extern`, then the definition in that translation unit is an _inline definition_. An inline definition does not provide an external definition for the function, [...]". But it talks about _declarations_, not _definitions_. -- So you're right, this function has no external linkage. – the busybee Aug 15 '23 at 09:35
  • You should also be getting a Rule 8.10 violation - an ``inline`` function not declared as ``static``. Fix that, and this one goes away too... – Andrew Aug 16 '23 at 07:36
  • Thanks @Andrew, I know it, but the question is still there. – Home of the Brave Aug 17 '23 at 00:04
  • I will consult the MISRA team on this... – Andrew Aug 17 '23 at 06:59
  • 1
    @Andrew My take is that C99 6.7.4 specifies a term called "inline definition", which may or may not get defined externally. Supposedly this allows a local definition of the function, which overwrites/replaces any existence of the same function in other translation units. Though apparently it is unspecified which one that actually gets called, which would be a depressing state of affairs in safety-related software, even if it is not UB as claimed by C99 Annex J. I think MISRA C 8.10 is correct to enforce `static` to block this ambiguous behavior, though I'm not sure if is correct to claim UB. – Lundin Aug 18 '23 at 10:08
  • Sounds like a good, separate "language-lawyer" question for SO. – Lundin Aug 18 '23 at 10:10

1 Answers1

6

MISRA C enforces prototypes since it allows C90 where non-prototype function declarations were a huge problem. But also to avoid linker errors and avoid inconsistencies between declaration and definition. Consider for example inline void f() called as f(123) - valid C99 but leading to mysterious linker errors.

8.4 specifically is about enforcing a function declaration before writing a function declaration with external linkage. Most of the time you do not want an inline function to have external linkage. In fact another MISRA C rule 8.10 enforces inline functions to always have internal linkage by declaring them static - and the reason for that is to avoid undefined behavior, as stated by that rule's rationale.

Another feature and/or subtle safety problem is that in case you define a function inside a header, that function will exist in every translation unit that includes the header. So you end up with multiple functions with the same name - typically leading to a linker error. Unless you also give the function internal linkage, in which case the C compiler/linker will utilize name mangling to separate the different functions.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Thanks. According to https://en.cppreference.com/w/c/language/inline , it has: "If a non-static function is declared inline, then it must be defined in the same translation unit. The inline definition that does not use extern is not externally visible and does not prevent other translation units from defining the same function. This makes the inline keyword an alternative to static for defining functions inside header files, which may be included in multiple translation units of the same program. " So I assume it is legal after C99, not sure what is the "undefined behavior" you mentioned. – Home of the Brave Aug 15 '23 at 08:56
  • @HomeoftheBrave It takes some serious language-lawyering from C99 6.7.4 §6 to get to the bottom of it. MISRA has the ambition to list a rule for every case of UB in the C language. However, the broken, so-called "annex J" of the standard does list this as UB (the clutter in §J2) , but I wonder if it isn't actually unspecified behavior because I find nothing about UB in the actual 6.7.4. Annex J might in turn have "poisoned" MISRA C - it's full of editor mistakes from C99 to C23. This sounds like material for a separate question. – Lundin Aug 15 '23 at 09:13
  • 1
    There is no such ambition - we have a general Rule (R.1.3) that covers most cases... specific Rules are provided where additional guidance is considered worthwhile. Likewise, I agree that the C standard has blurred the distinction between UDB and USB... – Andrew Aug 15 '23 at 09:17
  • @Andrew And yet there is MISRA C:2012 Annex H which at least I assumed was a pretty exhaustive list of UB. Is this not to be trusted then, just as ISO 9899 Annex J is not to be trusted? – Lundin Aug 15 '23 at 09:19
  • (That is, it's of course impossible to assemble a list of all implicit UB in C, which is everything not covered by the standard as well as every possible constraint violation. But it ought to be possible to assemble a list of all explicit UB.) – Lundin Aug 15 '23 at 09:25
  • Which I think MISRA's Annex H does? If you know of any errors or omissions, please let us know :-) – Andrew Aug 16 '23 at 07:35
  • @Andrew You said that MISRA has no ambition to cover all causes of UB and yet that is exactly what Annex H does (or should do, anyway). And most of the rules/directives that at a glance "do not make sense" often came up as a counter-measure against some form of explicit poorly-defined behavior. And when rules lack a proper rationale there's at least a reference to Annex H in most cases. – Lundin Aug 16 '23 at 08:26
  • What I meant is we won't be adding lots of "Don't use this specific UD/USB" - we have one catch all "Don't use any UD/USB" that covers most of them... :-) – Andrew Aug 16 '23 at 12:13
  • @Andrew I would imagine that the static analysis tool vendors would appreciate separate rules instead of one single "monster rule" to cover it all? For more precise diagnostics. – Lundin Aug 16 '23 at 13:05
  • To be honest, one Rule (to Rule them all?) is fine... Day job hat ;-) – Andrew Aug 16 '23 at 13:33
  • 1
    @Andrew Rule 1.1 "Do not write any bugs." :) – Lundin Aug 16 '23 at 13:46