Reputation: 4850
Consider the following example:
extern void not_defined();
void f() {
not_defined();
}
int main() {}
If I were to compile and link the above program, I get a linker error undefined reference to not_defined()
. (https://godbolt.org/z/jPzscK7ja)
This is expected because I am ODR using not_defined
.
However, if I make f()
a inline function, the program compiles and links correctly (https://godbolt.org/z/qEEob9ann):
extern void not_defined();
// inline is added here
inline void f() {
not_defined();
}
int main() {}
This also works for inline definitions of member functions (since they are also inline?) (https://godbolt.org/z/xce3neofW):
extern void not_defined();
struct C {
void f_member() {
not_defined();
}
void recurse() {
f_member();
};
};
inline void f_free_inline() {
not_defined();
}
// void f_free() {
// not_defined();
// }
int main() {
C c;
// f_free_inline();
}
If I were to uncomment f_free
or f_free_inline()
in main()
it no longer works . This effects seem to be even transitive, since recurse
calls f_member
, and f_member
calls not_defined()
.
So my questions is, what's special about inline functions that makes this possible?
EDIT:
This trick cease to work if the inline function is inside the purview of a module (even if it is not exported):
export module mod_interface;
extern void not_defined();
export inline void f_module() {
not_defined();
}
The above would result in a linker error for not_defined
.
This is sad because I am porting a library to modules and suddenly got a lot of undefined references. Some of those functions in it were wrapped inside inline functions.
Upvotes: 2
Views: 712
Reputation: 14589
Standard is not only a set requirements for compiler behaviour. It's also a set of requirements to programmer's behaviour.
GCC's linker wouldn't complain if a function X()
that ODR-used a missing symbol Y
is inline
or static
function, as there is no possibility that they would be called from outside module if it has local linkage and negligible, if it was inline.
But lo! Disregard the fact that you're able to compile and run this program, your program is ill-formed according to standard. Only in case of inline function X()
linker would optimize it out as soon as it didn't found a reference to inline X()
in any of compile units.
If it's not inline function, it would stay until final stage of linking and the missing name will be looked up through modules. Implementation of your compiler is such that it would be searched for before excluding unused X()
. Some other compiler may behave differently.
That's why it's "ill-formed" code, but "no diagnostic required". Diagnostic is possible with certain implementations. Some implementations will diagnose both cases as an error and some are unable to detect your deception. Some implementations will detect the problem only if you had more than one compile unit.
Upvotes: 2