1
votes

There is a constraint 6.7.4(p3):

An inline definition of a function with external linkage shall not contain a definition of a modifiable object with static or thread storage duration, and shall not contain a reference to an identifier with internal linkage.

Consider the following example:

static const int i = 10;

void do_print(void);

inline void do_print(void){
    printf("%d/n", i); //Reference to an identifier with internal linkage
                       //constraint violation
}

DEMO

Here the inline definition of a function with external linkage uses an identifier with internal linkage. So according to 5.1.1.3(p1):

A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint, even if the behavior is also explicitly specified as undefined or implementation-defined.

I expected the violation of this constraint is reported by the compiler somehow (some warning). But the code compiles just fine with no warnings or some other message produced.

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

2
Apple LLVM 10.0.1 with clang-1001.0.46.3 warns with -pedantic: “warning: static variable 'i' is used in an inline function with external linkage [-Wstatic-in-inline]”. - Eric Postpischil
GCC has a bug. Nothing more to it. Clang reports it - StoryTeller - Unslander Monica
@Ctx - If one TU has an external definition for that function, it will use the static for that TU. Whereas the inline definitions will use the static that is local to the TU they appear in. Since it's unspecified whether the implementation uses the inline definition or the external definition of the function, this can lead to problematic code. And such is UB. - StoryTeller - Unslander Monica
@Ctx: The standard explicitly permits an inline definition on one translation unit and an external definition in another translation unit. (An inline definition is a definition using inline while no file-scope declaration in the translation unit has extern or does not have inline.) And it says “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.” - Eric Postpischil
@Ctx: Yes, I think that is a GCC bug. C 2018 6.7.4 7 says “An inline definition does not provide an external definition for the function, and does not forbid an external definition in another translation unit.” - Eric Postpischil

2 Answers

4
votes

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.

4
votes

cppreference has a paragraph that explains the rationale behind that:

If a function is declared inline in some translation units, it does not need to be declared inline everywhere: at most one translation unit may also provide a regular, non-inline non-static function, or a function declared extern inline. This one translation unit is said to provide the external definition. One external definition must exist in the program if the name of the function with external linkage is used in an expression, see one definition rule.

If the external definition exists in the program, the address of the function is always the address of the external function, but when this address is used to make a function call, it's unspecified whether the inline definition (if present in the translation unit) or the external definition is called.

A note also says (emphasize mine):

The inline keyword was adopted from C++, but in C++, if a function is declared inline, it must be declared inline in every translation unit, and also every definition of an inline function must be exactly the same (in C, the definitions may be different, as long as the behavior of the program does not depend on the differences). On the other hand, C++ allows non-const function-local statics and all function-local statics from different definitions of an inline function are the same in C++ but distinct in C.

That means that if a local inline function uses a static const value in one translation unit, a non inline function with same name could be defined in a different translation unit with a different value for the static const variable, leading to explicit UB because it is unspecified whether the compiler will use the local inline of the global non inline version.