5
votes

From the C18 standard:

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.

Then we read:

The declaration of an inline function with external linkage can result in either an external definition, or a definition available for use only within the translation unit. A file scope declaration with extern creates an external definition.

I've written a bit of code to check if the function is actually inline or not. I've used this restriction to find out:

An inline definition of a function with external linkage shall not contain a definition of a modifiable object with static or thread storage duration, and shall not contain a reference to an identifier with internal linkage.

This is the code:

static int n = 5;

void inline foo() { n = 66; }
void inline foo();    // remove 'inline' in second version

int main() {
    return 0;
}

When compiling this I get a warning saying that the inline function is using a static object, which means that foo() is, effectively, an inline function, and so it provides an inline (not external) definition. However, when I remove the inline specifier from the indicated line, I don't get the warning anymore. According to the standard, it's not an inline definition, so I guess it's providing an external definition.

What the standard is not saying, or at least I cannot see it, is whether an inline function that provides an external definition stops being an inline function, or not. According to my test, it does stop being an inline function.

If I'm right in my conclusions, which I don't know, another question arises: why an extern inline function is a useless thing?

2

2 Answers

2
votes

In the question you try things in the compiler to try and deduce the language rules. This is generally a bad idea, because (a) in many situations the effect of breaking the rules is hard to observe, and (b) the compiler might be bugged. Instead, the Standard is an authoritative source for what the language rules are, so the question should be answered by referring to the Standard.


Moving on: your code contains a constraint violation of C11 6.7.4/3, which you quoted in your question. The effect of a constraint violation is that the compiler must issue a diagnostic, which it did.

Then you ask about some modification, I assume you mean the following code:

static int n = 5;
void inline foo() { n = 66; }
void foo();
int main() { return 0; }

As covered by the first sentence you quoted (from 6.7.4/7), the definition of foo() is not an inline definition, because it is not true that all of the file-scope declarations in the TU include the inline specifier without extern. (That sentence is intended to deny the antecedent).

Since it is not an inline definition, there is no problem with n = 66 and the code is correct.

What the standard is not saying, or at least I cannot see it, is whether an inline function that provides an external definition stops being an inline function, or not

An inline function definition is never an external definition. This is clearly stated in 6.7.4/7 "An inline definition does not provide an external definition for the function".

Maybe your confusion arises from conflating the concepts "inline function definition" and "function definition with the inline specifier".

another question arises: why an extern inline function is a useless thing?

If you mean the keywords extern inline that is another topic that was not touched on by this question, see here. Inline functions with external linkage are certainly not useless .

1
votes

I feel I need to answer myself, as this is even more complex than I expected at the beginning, and new facts have arisen during my research since I wrote the question. This is more like my own conclusions, but I feel I'm in the right path. So I need to share. Feedback and confirmation/rejection will be most appreciated.

In the first place, take a look at this code:

void inline foo() { return; }

int main() {
    foo();
    return 0;
}

It might seem like a simple code, but the fact is that it doesn't compile. Well, actually, it compiles, but it fails in the linker step. Why? Let's read the full difficult-to-understand paragraph from the standard:

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. 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, and does not forbid an external definition in another translation unit. An inline definition provides an alternative to an external definition, which a translator may use to implement any call to the function in the same translation unit. It is unspecified whether a call to the function uses the inline definition or the external definition.

From "It is unspecified whether a call to the function uses the inline definition or the external definition" we get the answer of why it didn't compile (link) well. My implementation (GCC) chose the external version. And the linker doesn't know about such external function.

The standard says an inline definition "does not forbid an external definition in another translation unit". Actually it doesn't, but it even makes mandatory to define it elsewhere if the function is called from the present translation unit and the implementation chooses to call the external version.

Then, another question arises: if the implementation choses to call the external definition, or the inline definition, why is it necessary to define both? Well I found the answer in GCC documentation: you never know when one will be chosen or the other. For instance, GCC chooses the external version when no optimizer switches are indicated. For many optimized code configurations, inline versions will be chosen.

And regarding the question about why inline extern functions could be useless, actually they are not. External functions can also be inlined. Check this document: https://gcc.gnu.org/onlinedocs/gcc/Inline.html

An external inline function can be used and inlined from other translation units, it just doesn't create an inline definition. Inline definitions are only useful when you want to have alternative versions of a function that are used depending on optimization switches, for instance.

However, I think the standard is not very clear about the inlining of external inline functions. What GCC does, for example is: non-static inline functions are not inline functions, unless they have inline and extern specifiers in the declaration (not in the external definition) of the function.