Bug 1494052 - inline function causes undefined symbol error if not static, or -O level given
Summary: inline function causes undefined symbol error if not static, or -O level given
Keywords:
Status: CLOSED NOTABUG
Alias: None
Product: Fedora
Classification: Fedora
Component: gcc
Version: 26
Hardware: x86_64
OS: Linux
unspecified
low
Target Milestone: ---
Assignee: Jakub Jelinek
QA Contact: Fedora Extras Quality Assurance
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2017-09-21 12:04 UTC by Erkki Ruohtula
Modified: 2017-09-22 09:49 UTC (History)
7 users (show)

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2017-09-21 12:20:19 UTC
Type: Bug
Embargoed:


Attachments (Terms of Use)

Description Erkki Ruohtula 2017-09-21 12:04:52 UTC
Description of problem:
An inline function causes undefined symbol error if not static, or -O level given

Version-Release number of selected component (if applicable):

GCC 7.1.1-3

How reproducible:

Reliably reproducible,

Steps to Reproduce:

Compile the following shortened sample without options

__inline__ int doubler(int number)
{
  return number * 2;
}


int main(int argc, char**argv)
{
    return doubler(argc);
}


Actual results:

$ LC_ALL=C gcc inline-bug.c 
/tmp/cclHMpkK.o: In function `main':
inline-bug.c:(.text+0x15): undefined reference to `doubler'
collect2: error: ld returned 1 exit status

Expected results:

Compilation should succeed w/o messages.

Additional info:

If any -O option except -O0 is added, the problem disappears.
From assembly, it looks like GCC omits the code for the function, but nevertheless emits a call to it.

Comment 1 Jonathan Wakely 2017-09-21 12:11:34 UTC
Your program is not valid. See "Different semantics for inline functions" at https://gcc.gnu.org/gcc-5/porting_to.html

Comment 2 Erkki Ruohtula 2017-09-21 16:19:58 UTC
I guess that strange behaviour is permitted by C99, but reading about inline in the latest free draft (http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf) suggests that GCC could also do the right thing, and still be compliant:

>>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 6.7.4 "semantics").

GCC could always use the inline definition, which apparently is what it does when any optimization is enabled.

Comment 3 Jonathan Wakely 2017-09-21 16:25:22 UTC
When optimization is enabled the function gets inlined, so no definition is needed.

Comment 4 Erkki Ruohtula 2017-09-22 04:51:49 UTC
Yes, but then the same very valid-looking code may call an entirely different function, depending on whether optimization is on. Could cause fun situations in debugging. Given this situation, it seems to me an inline without static should cause a warning (in C mode), because it is likely the user did not expect this obscure behaviour. The writers of the code that prompted me to make this bug report certainly didn't.

Comment 5 Jakub Jelinek 2017-09-22 09:49:15 UTC
When using the C99 inline semantics without static, it is expected that exactly one TU provides the extern definition that then can be used whenever some call in that TU or other TU couldn't be inlined.  There are many reasons why something isn't inlined, inlining is just an optimization; inlining could not be done because the user asked not to (-fno-inline, default with -O0), or the inline function's address is stored in a function pointer and the optimizers can't track it to all indirect calls through that function pointer, or the inline function contains something that prevents inlining (e.g. alloca), or is too large or the compiler doesn't see it as beneficial.

If you want to unconditionally force inlining, GCC provides a way - inline __attribute__((always_inline)).

Note, this isn't much different from the old GNU inline semantics, inline is an optimization there too and one needs to provide a fallback if inlining fails.


Note You need to log in before you can comment on or make changes to this bug.