C++ Logo

std-proposals

Advanced search

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

From: Thiago Macieira <thiago_at_[hidden]>
Date: Thu, 04 May 2023 18:22:17 -0700
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.

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

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.

-- 
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
   Software Architect - Intel DCAI Cloud Engineering

Received on 2023-05-05 01:22:19