4

I've read here:

and it stated:

A function defined with inline on its own. Stand-alone object code is always emitted. You can only write one definition like this in your entire program. If you want to use it from other translation units to the one where it is defined, you put a declaration in a header file; but it would not be inlined in those translation units.

however, in my minimal reproducible example:

test.c

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

receives

cc -std=c99 -Wall -Wextra -Wshadow -Wpedantic test.c -o test                                                                                                                   
Undefined symbols for architecture x86_64:                                                                                                                                     
  "_foo", referenced from:                                                                                                                                                     
      _main in test-febb67.o                                                                                                                                                   
ld: symbol(s) not found for architecture x86_64                                                                                                                                
clang: error: linker command failed with exit code 1 (use -v to see invocation)                                                                                                
make: *** [Makefile:4: test] Error 1 

so it seems that code is not being properly emitted. The same problem is also present in gcc. What is going on here? Specifically, I'd like to know, what are the differences here:

This fixes it.

inline foo(void) { /* definition */ }
foo(void);

This fixes it.

foo(void) { /* definition */ }
inline foo(void);

and my case: Wrong.

inline foo(void) { /* definition */ }

I saw here that it could be because it has external linkage, but an external definition is not provided; however, this is only references in main and in a single translation unit (internally). Where is the symbol unresolved and why? It must be the call in main.

I’ve found that with -O2 turned on, there is no issue, and this is because code is not originally being emitted and it is not being inlined by default.

This did not answer my question. I am not asking about the static inline fix, except in why it is treated differently than just inline in this case, which I still do not understand. Why does that always cause an emission?

I’d like to understand why this is a problem in the first place, since I’d read that it would emit code. Is this present anywhere explicitly in the standard?

user129393192
  • 797
  • 1
  • 8

1 Answers1

3

Is this present anywhere explicitly in the standard?

This is what the C language specification says about it:

Any function with internal linkage can be an inline function. 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.

(C23 6.7.4/7; emphasis added)

and

An external definition is an external declaration that is also a definition of a function (other than an inline definition) or an object. If an identifier declared with external linkage is used in an expression (other than [...]), somewhere in the entire program there shall be exactly one external definition for the identifier [...]

(C23 6.9/5; emphasis added)

That plays out for your code like so:

  • By virtue of being declared at file scope without static, function foo has external linkage.

  • Because foo is declared inline but not explicitly extern, the provided definition is an inline definition, not an external definition.

  • Your main() calls foo, which has external linkage, so somewhere in the whole program there needs to be an external definition of that function, but there isn't. Therefore your program is non-conforming.

so it seems that code is not being properly emitted.

No, you've got that mixed up. Rather: the code is properly not being emitted. An inline definition is not an external definition, and the compiler needs to leave room for an external definition to be provided by another translation unit.

GCC is not required to reject incorrect programs, and it's reasonable to imagine that it might figure out what to do with yours, but it is completely within its rights and the spec to reject the program. That it accepts the program when you enable optimization is fully consistent.

This fixes it.

inline foo(void) { /* definition */ }
foo(void);

Yes. Now the condition "If all of the file scope declarations for a function in a translation unit include the inline function specifier without extern" is no longer satisfied, so the definition is an external one, not an inline one.

This fixes it.

foo(void) { /* definition */ }
inline foo(void);

Same. Now foo's definition is an external one.

Another solution in the same vein:

extern inline foo(void) { /* definition */ }

And of course, you can avoid the issue by giving foo internal linkage. There are several variations on that, but this is one of them:

static inline foo(void) { /* definition */ }

.

If you think this is all a bit weird, then you're right. It's usually best to avoid the issue by giving your inline functions internal linkage (by declaring them static). But if you don't do that, then a reasonable usage pattern is similar to that for declaring and defining external variables:

  • put the (inline) definition in a header.
  • choose one translation unit to provide the external definition. In this TU,
    • #include the header, and
    • redeclare the function with the extern specifier.

That gets you the needed external definition without duplicating any code, but I don't see much to recommend it over simply making the definition static.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • You could also just forget you ever heard of `inline`. Modern compilers are pretty good at figuring out opportunities for optimizations without your help. Most will happily choose functions to inline at their own discretion, without your input (or despite your input). – John Bollinger Aug 04 '23 at 15:22
  • Which line is it that also says that a `static inline` will always emit code? Also, is there a line that explicitly states that `extern inline` provides an external definition? How would that be different from having one without any identifiers at all (as in my examples)? – user129393192 Aug 04 '23 at 22:33
  • @user129393192, "emits code" is not a requirement that the spec places on anything. The already-quoted sections establish that `extern inline foo() { /*body*/ }` provides an external definition: it is an external declaration (because function and not `static`) that is a definition (because the function body is provided) and is not an inline definition (because explicitly `extern`). – John Bollinger Aug 04 '23 at 23:10
  • Understood. Is the `static inline` case covered here as well? – user129393192 Aug 05 '23 at 00:02
  • I believe the strangest thing here is that `inline int foo() {}` `int foo();` makes the one with `inline` not an `inline` definition. Seemingly counter-intuitive. – user129393192 Aug 05 '23 at 00:03
  • @user129393192, any function declared with `inline` is an "inline function", which is merely a hint to the compiler that calls to the function should be as fast as possible. Per the quoted spec text, a function definition is an inline definition if and only if *all* the declarations in the TU do contain `inline` and do not contain `extern`. Declarations that include `static` cannot also include `extern`, and preclude other declarations in the same TU that include `extern`. – John Bollinger Aug 05 '23 at 01:22
  • Are you also getting the information on`static` from the standard? – user129393192 Aug 05 '23 at 01:37
  • @user129393192, most of what I'm telling you about `static` is in the sections of the language spec that I have already quoted. A few things are drawn from other parts of the spec. Overall, yes, it's all from the spec. – John Bollinger Aug 05 '23 at 04:57
  • So the part you are referencing is “any function with internal linkage can be an inline function.” don’t see how this states that code will be emitted, or as you say, a non-inline definition provided for linkage. – user129393192 Aug 05 '23 at 05:09
  • @user129393192, did I not already say that code being emitted is not a requirement placed by the spec on *anything*? The spec describes the the syntax and constraints of the language and the behavior of those programs whose behavior is defined at all. All details of actual binaries generated by a compiler are characteristic of that compiler, not of the language itself. – John Bollinger Aug 05 '23 at 17:12
  • I was responding to that, and simply asking what piece of your spec talks of the `static inline`. – user129393192 Aug 05 '23 at 17:18
  • @user129393192, `static inline` is not a joint thing. It is `static`, with the implications of that, and `inline`, with the implications of that. The only thing that places any significance at all on their combination is in the quoted section of section 6.7.4, but that's moot, because definitions of functions with internal linkage have all the properties of inline definitions anyway whether they actually are inline definitions or not. – John Bollinger Aug 05 '23 at 17:29
  • So I’m not sure I understand. Why is `static inline` another solution that works? `static inline int foo(){} int main(){foo();}`? What speaks on that? – user129393192 Aug 05 '23 at 17:31
  • @user129393192, you are looking at it the wrong way around. Function calls work in programs whose behavior is defined at all. The spec does not say specifically that they work for functions declared `static` and `inline` because it does not need to do. That case is not special in this regard. From the perspective of the spec, the combinations that you observed not to work all fail to conform to the spec, and therefore cause programs containing them to have undefined behavior. The spec does not speak to what will happen when you compile or run such a program. Errors are one possibility. – John Bollinger Aug 05 '23 at 17:40
  • I see, and the compiler failing to emit code is one such error. That is so interesting that all these compilers behave the same in these ways. So the line "any function with internal linkage can be an inline function" with no restrictions, is what causes this? – user129393192 Aug 05 '23 at 17:49
  • @user129393192, that particular line is pretty much fluff. I take its main purpose to be for contrast with the provisions for external functions with respect to `inline`. To recast some of my previous comments: if a function has internal linkage, *it does not matter* whether its definition is an inline one. The spec makes the same provisions for it and places the same requirements on it either way. – John Bollinger Aug 05 '23 at 19:33
  • I see. This comes from elsewhere in the spec? – user129393192 Aug 05 '23 at 19:53
  • @user129393192, it is a conclusion drawn from my analysis of the spec. You are welcome to perform your own analysis if you like, and indeed, I encourage you to do so. Especially if you are disinclined to accept my assurances that everything I am telling you about the provisions of the C language is supported by the spec. But if in fact you have such a disinclination then I don't understand the reason for the barrage of questions. – John Bollinger Aug 05 '23 at 20:20