clinker-errorsfunction-definitionlinkageinline-functions

inline keyword causes linker error in Clion


I have a strange error concerning the inline keyword, this is just a sample code I wrote:

#include <stdio.h>
#include <stdint.h>

uint8_t inline LIB_MATH_BTT_u8GetMSBSetBitPos(uint32_t args_u32Variable)
{
    uint8_t local_u8MSBSetBitPos = 0;
    args_u32Variable = args_u32Variable >> 1;
    while (args_u32Variable != 0)
    {
        args_u32Variable = args_u32Variable >> 1;
        local_u8MSBSetBitPos++;
    }
    return local_u8MSBSetBitPos;
}

int main() {
    int x  = LIB_MATH_BTT_u8GetMSBSetBitPos(17);
    printf("%d", x);
    return 0;
}

I ran this code on Visual studio 2022 and this was the result:

4

and the same code was run on Clion with toolchain from MinGW and this was the result:

====================[ Build | untitled | Debug ]================================
"C:\Program Files\JetBrains\CLion 2022.3.2\bin\cmake\win\x64\bin\cmake.exe" --build C:\Users\User\CLionProjects\untitled\cmake-build-debug --target untitled -j 12
[1/2] Building C object CMakeFiles/untitled.dir/main.c.obj
[2/2] Linking C executable untitled.exe
FAILED: untitled.exe 
cmd.exe /C "cd . && C:\PROGRA~1\JETBRA~1\CLION2~1.2\bin\mingw\bin\gcc.exe -g  CMakeFiles/untitled.dir/main.c.obj -o untitled.exe -Wl,--out-implib,libuntitled.dll.a -Wl,--major-image-version,0,--minor-image-version,0  -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 && cd ."
C:\Program Files\JetBrains\CLion 2022.3.2\bin\mingw\bin/ld.exe: CMakeFiles/untitled.dir/main.c.obj: in function `main':
C:/Users/User/CLionProjects/untitled/main.c:6: undefined reference to `LIB_MATH_BTT_u8GetMSBSetBitPos'
collect2.exe: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.

so I am so confused why the same code was run successfully on Visual Studio 2022 but failed on Clion.

PS. the code worked successfully on Clion if I removed the inline keyword.


Solution

  • From the C Standard (6.7.4 Function specifiers)

    7 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.

    So as this function

    uint8_t inline LIB_MATH_BTT_u8GetMSBSetBitPos(uint32_t args_u32Variable)
    

    has external linkage then the linker searches its external definition and does not find it.

    Either you need to provide an external definition of the function (defining it with storage class specifier extern) or you could declare the function with internal linkage as for example

    static uint8_t inline LIB_MATH_BTT_u8GetMSBSetBitPos(uint32_t args_u32Variable)
    

    and in this case it will be enough to use the inline definition of the function with internal linkage.