C++ Logo


Advanced search

Re: [std-proposals] Drop same sequence of tokens for inline

From: David Brown <david_at_[hidden]>
Date: Fri, 5 May 2023 09:03:45 +0200
On 05/05/2023 03:22, Thiago Macieira via Std-Proposals wrote:
> On Thursday, 4 May 2023 17:54:12 PDT Alejandro Colomar wrote:
>> I still don't understand how use of static within an inline function
>> is not UB. Maybe in C++ it's useful. In C, I never found a need for
>> 'static' within an inline function.
> Indeed, but I'd say that it's a consequence of how statics inside inline were
> defined in C. That removed their usefulness, therefore they don't get used. The
> warning also probably exists because people tried to use it and have run into
> issues.

Modifiable statics inside non-static inline functions are not allowed in
C - it's a constraint error.

 From the standard, 6.7.4 ("Function specifiers") :

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.

If the inline function is "static inline", then it is just like any
other static function.

You can have "static const" variables inside a non-static inline
function - in C, these are initialised with constant values before
main() starts up.

> Static variables inside inline functions aren't that common in C++, except for
> a few very common patterns, such as that of singletons:
> class MySingleton
> {
> MySingleton();
> ~MySingleton();
> public:
> static MySingleton *instance()
> {
> static MySingleton self;
> return &self;
> }
> };
> Replacing this with a namespace- or class-scope static is not the same thing
> because it changes the lifetime of the object and it may be important that
> this constructor not be run at load time. At a minimum, this is lazy
> initialisation and conforms to "Don't Pay for What You Don't Need" ethos. It
> is possible to write code that keep the ethos and uses a non-vague variable,
> but it requires writing more code; a great deal more if you want to also have
> the thread- and exception-safety guarantee the code above gives you
> implicitly.
>> Is there any way to get UB without 'static' in compatible definitions such
>> as these?
> Yes, it's possible. The compiler is allowed to break your function into blocks
> and inline a portion of it and not another. GCC does this quite a lot when it
> decides some expensive code is unlikely. Take this example:
> void other();
> inline void f1(int i)
> {
> if (i)
> other();
> else
> throw i;
> }
> Modern GCC considers that throwing code is unlikely, just as if you had a
> [[likely]] attribute on that first if or __builtin_expected, so it moves the
> else branch to a separate block. You can see it in action in
> <https://gcc.godbolt.org/z/ns6Ph51Go>; note how there's a symbol with "[clone
> .cold]" suffix (if you turn on directives, you'll see that it's even in an
> entirely different section of the executable).

This is a completely different concept from the "inline" keyword.

Historically, the "inline" keyword was added to C and C++ as a compiler
hint, in the days of weaker compilers. These days, compilers make their
own decisions (influenced by compiler flags, pragmas, compiler-specific
attributes, etc.) about code generation. The compiler will inline some
or all of a function with a total disregard for the "inline" keyword.
It will also happily outline code that happens to be within a function
declared "inline".

It is important to concentrate on the semantics of "inline" - what it
actually means in the code. (It is also important to consider what it
means to people reading the code.) Leave the code generation details to
the compiler.

> Note also how this is not a new function obeying the regular calling
> convention, but really just a portion of the original and it expects the value
> to be thrown to be in the %eax register. So two different TUs did something
> different and innocent before that if, the register allocation might be
> different and result in garbage. UB.
> However, I do note in this case that the [clone .cold] applies to f(int),
> which is not the inline function but the non-inline one into which got f()
> inlined. So this particular version of the compiler wouldn't suffer from that
> problem.

Received on 2023-05-05 07:03:54