Inline Function Linkage

Inline function linkage

When the function in the header is not inline, then multiple definitions of this function (e.g. in multiple translation units) is a violation of ODR rules.

Inline functions by default have external linkage. Hence, as a consequence of ODR rules (given below), such multiple definitions (e.g. in multiple translation units) are Okay:

$3.2/5- "There can be more than one
definition of a class type (Clause 9),
enumeration type (7.2), inline
function with external linkage

(7.1.2), class template (Clause 14),
non-static function template (14.5.6),
static data member of a class template
(14.5.1.3), member function of a class
template (14.5.1.1), or template
specialization for which some template
parameters are not specified (14.7,
14.5.5) in a program provided that each definition appears in a different
translation unit, and provided the
definitions satisfy the following
requirements. Given such an entity
named D defined in more than one
translation unit, then

— each definition of D shall consist
of the same sequence of tokens; and [...]

How the linker treats inline functions is a pretty much implementation level detail. Suffice it to know that the implementation accepts such mulitple defintions within the limitations of ODR rules

Note that if the function declaration in header is changed to 'static inline....', then the inline function explicitly has internal linkage and each translation unit has it's own copy of the static inline function.

Clarification over internal linkage of inline functions in C

TL;DR: GCC still defaults to its old semantics of inline, in which an inline function is still compiled as an externally visible entity. Specifying -std=c99 or -std=c11 will cause GCC to implement the standard semantics; however, the IBM compiler does not conform to the standard either. So linking will still fail, but with a different error.


Since C99, a function declaration with no declared linkage does not generate a function object. The inline definition will only be used with inline substitution, and the compiler is not obliged to perform this optimisation. It is expected that an external definition of the function exists in some other translation unit, and such a definition must exist if the function object is used, either by taking its address or by being called in a context where the compiler chooses not to perform the inline substitution.

If the inline function is declared with either static or extern, then a function object is compiled, with the indicated linkage, thereby satisfying the requirement that the function object be defined.

Prior to C99, inline was not part of the C standard, but many compilers -- particularly GCC -- implemented it as an extension. In the case of GCC, however, the semantics of inline differed slightly from the above exposition.

In C99 (and more recent), an inline function with no linkage specification is only 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." §6.7.4p7). But in the GCC extension, an inline function with no linkage specification was given external linkage (just like a non-inline function declaration). GCC then special-cased extern inline to mean "do not generate a function object", which is effectively the same as standard C99's handling of an inline function with neither extern nor static modifiers. See the GCC manual, particularly the last section.

This is only still important because GCC still defaults to using its original inline semantics unless you specify that it should conform to some C standard (using, for example, -std=c11) or disable the GNU inline semantics using -fno-gnu89-inline.

The example code, which I understand is taken from the IBM i7.1 compiler documentation, does not correctly reflect any C standard. The two definitions of foo as inline functions do not generate any actual function named foo, so the use of &foo must refer to some externally-defined foo, and there isn't one in the program. GCC will report this issue if you tell it to use C11/C99 semantics:

$ gcc -std=c99 a.c b.c
/tmp/ccUKlp5g.o: In function `g':
a.c:(.text+0xa): undefined reference to `foo'
a.c:(.text+0x13): undefined reference to `foo'
/tmp/cc2hv17O.o: In function `main':
b.c:(.text+0xa): undefined reference to `foo'
b.c:(.text+0x13): undefined reference to `foo'
collect2: error: ld returned 1 exit status

By contrast, if you ask for Gnu inline semantics, both translation units will define foo, and the linker will complain about a duplicate definition:

$ gcc -std=c99 -fgnu89-inline a.c b.c
/tmp/ccAHHqOI.o: In function `foo':
b.c:(.text+0x0): multiple definition of `foo'
/tmp/ccPyQrTO.o:a.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status

Also note that GCC does not inline any function by default. You must provide some optimization option in order to enable function inlining. If you do so, and you remove the use of the address operator, you can get the program to compile:

$ cat a2.c
#include <stdio.h>
inline int foo() { return 3; }
void g() {
printf("foo called from g: return value = %d\n", foo());
}
$ cat b2.c
#include <stdio.h>
inline int foo() { return 4; }
void g();
int main() {
printf("foo called from main: return value = %d\n", foo());
g();
return 0;
}

$ # With no optimisation, an external definition is still needed:
$ gcc -std=c11 a2.c b2.c
/tmp/cccJV9J6.o: In function `g':
a2.c:(.text+0xa): undefined reference to `foo'
/tmp/cct5NcjY.o: In function `main':
b2.c:(.text+0xa): undefined reference to `foo'
collect2: error: ld returned 1 exit status

$ # With inlining enabled, the program works as (possibly) expected:
$ gcc -std=c11 -O a2.c b2.c
$ gcc -std=c11 -O1 a2.c b2.c
$ ./a.out
foo called from main: return value = 4
foo called from g: return value = 3

As indicated by the IBM documentation, the rules for C++ are distinct. This program is not valid C++ because the definitions of foo in the two translation units differ, but the compiler is not obliged to detect this error and the usual Undefined Behaviour rules apply (i.e., the standard doesn't define what will be printed). As it happens, GCC seems to show the same results as i7.1:

$ gcc -std=c++14 -x c++ a.c b.c
$ ./a.out
foo called from main: return value = 3, address = 0x55cd03df5670
foo called from g: return value = 3, address = 0x55cd03df5670

Does inline determine internal linkage?


I'm trying to extern an inline function.

There is no reason to use extern with a function. See storage duration - linkage. Functions have external linkage by default; in order to not have external linkage, something special needs to be done (i.e. put it in an anonymous namespace or declare it static). So the normal use of an inline function already exhibits external linkage with no need for the extern keyword.

How I thought it should work:

//a.cpp
inline void f(int) {}
//b.cpp
extern void f(int);
int main() { f(4); }

Then by reading this ("1) It must be declared inline in every translation unit.").

That reference is correct, but look up a bit more where it says "The definition of an inline function [...] must be present in the translation unit where it is accessed [...]." Your example has a declaration of f in b.cpp, but not a definition. If you are going to call f from b.cpp, you need the full definition in that translation unit, as in:

inline void f(int) {}

(This is the same code that exists in a.cpp.) If you leave off the curly braces, then you have a declaration but not a definition, making it illegal to call f from that translation unit.

Basically, it is a real pain to define an inline function outside a header file, unless you give it internal linkage. That's because each source file that uses the inline function would need its own copy of the function body, meaning that if you change the function, you need to make that change in multiple files. Oof. Don't do it. Define each of your inline functions in a header file. If you think you want to define one in a source file, you probably misunderstand what "inline" means.


What does "inline" mean?

As far as the compiler is concerned, the inline keyword means (almost) nothing. It is just a flag on a function definition that gets propagated into the object code so that the linker sees it. The compiler processes the function just as it would any other function. The function may be called normally, or calls to it might be inlined – just like any other function.

The one case where the compiler might do something with the inline flag is when a function is declared inline, is used, but lacks a definition. This is an error that can be caught before the linker takes over. It does not have to be caught by the compiler, but it can be. (If not caught by the compiler, it would be caught by the linker.)

Moving on to the linking stage. When the linker sees the inline flag, it suspends the one-definition rule for that function. The linker will expect to see a definition of the function in each translation unit that still uses the function after the compiler's optimizations. It gets to choose any one of those definitions to serve as the final implementation. Hence the reason that all definitions must match.

And that's about it. The inline keyword basically means that the function definition is in a header file. It tells the linker to not complain when that definition appears in multiple translation units, because that is expected.

Going back to the question, it looks like the intent was to declare an inline function whose definition would appear in only one translation unit. In other words, the function would be flagged as defined in multiple translation units, but the definition would be in only one. Kind of inconsistent there, if not outright contradictory.

Inline function definition with external linkage containing reference to a static object


The question is: Why is no diagnostic message produced in case of the constraint violation above?

Because your compiler is non-conforming in this regard.

That's really all there is to it. You have analyzed the text of the standard correctly, and applied it correctly to the code presented. A conforming implementation must emit a diagnostic about the reference to variable i by the inline implementation of do_print. An implementation that does not is, ergo, non-conforming.

I note at this point that some compilers are non-conforming in this general way -- omitting required diagnostics -- by default, while providing an option for turning on these mandatory diagnostics. This is the function of the -pedantic option in GCC, for example. However, I find that my (somewhat dated) version of GCC does not warn about your code even when -pedantic is specified.

Is an external linkage inline function really inline?

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 .

Inline functions and external linkage

The linkage of a name has nothing to do with where or how it is defined, just with where the name may be used to refer to a particular object or function.

Declaring a function inline does not force it to be inlined; it just relaxes the One Definition Rule to allow a definition in each translation unit in which it's used (and require one in each translation unit in which it's called), to make it easier to inline. It doesn't prevent a non-inline version being generated, if the compiler decides not to inline a particular call to it, or if you take the address of it.

So "external linkage" and "inline" are not exclusive; "external linkage" means that the function may be referred to in any translation unit, and "inline" means that it must be defined in any translation unit that calls it.

Inline functions with internal linkage?

This is how things are defined.

The inlined function would be inserted in the module where it is called. So, it can't access the private stuff in its module where it is defined.

If the inlined function is only used in that module(internal linkage). Then it is safe to grant it an access to the "private" stuff of that module.

Is there any practical difference between an inline function having internal and external linkage, with compiler optimization?

The important distinction here is that functions with internal linkage in various translation units are different functions, while inline function definitions with external linkage in various translation units all define the same function. This can certainly affect generated code, if only in that if a symbol is emitted (because, for instance, a pointer to the function is stored), external linkage it makes it a weak rather than local symbol.

However, the more significant difference is the possibility for ODR violations if “a” static inline function f is defined in a header file: any multiply-defined entity (e.g., a function template in a header file that is included more than once) that uses f is invalid because each definition uses a different f.

inline definition of function - external or internal linkage

Your example actually violates the one definition rule. In short, with an inline function you're allowed to have multiple definitions provided that each one:

  • is in a different translation unit (which you do have)
  • each definition has the same sequence of tokens (which you clearly do not)

So this is actually UB.

inline functions require there to be a definition in every translation unit where they are ODR used, so they can be completely defined in a header without a compilation unit for example.

This is not the same as "inlining" a function. inline only implies that the function is defined "in-line" with it's declaration. Any function will or will not be inlined at the discretion of your compiler.



Related Topics



Leave a reply



Submit