Date: Fri, 5 May 2023 17:49:08 +0200
On 05/05/2023 16:04, Alejandro Colomar via Std-Proposals wrote:
> Hi Thiago,
>
> On 5/5/23 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.
>
> Actually, I'm very confused about static within inline in C, since it seems to
> really be UB in one paragraph, and defined behavior a few paragraphs below:
>
> <https://port70.net/~nsz/c/c11/n1570.html#6.7.4p3>
> 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.
>
> From the above, it seems UB. But then paragraph 7 has a note:
>
> <https://port70.net/~nsz/c/c11/n1570.html#6.7.4p7>
> [...] It is unspecified whether a call to the function uses
> the inline definition or the external definition. 140)
>
> OK so far. Here's the note:
>
> <https://port70.net/~nsz/c/c11/n1570.html#note140>
> Since an inline definition is distinct from the corresponding
> external definition and from any other corresponding inline
> definitions in other translation units, all corresponding
> objects with static storage duration are also distinct in each
> of the definitions.
>
> What? Now the behavior seems to be defined to be distinct in all definitions
> instead of the "shall not" happen from 6.7.4p3. Still unspecified which one
> gets called.
You cannot have two definitions visible at the same time in a single
translation unit. If you have an inline definition of the function, you
cannot have a distinct external definition. You can write an inline
definition, and then an extern declaration for the function (without a
function body) - that makes the same body definition available as an
extern non-inlined function. Alternatively, you can have a non-inline
version defined in a different translation unit. In this case, the
function definitions can be different, and /that/ version's body can use
statics like a normal function.
It is always unspecified which one gets called - the currently available
inline version, or the non-inlined external definition.
(In case anyone has missed it, this is all about C, not C++. The
semantics of C++ "inline" are, IMHO, much simpler - if a function or
variable is declared "inline", it means there can be multiple
definitions of the same function or object (including initialisation) in
the program, and it is the compiler and linker's job to ensure that they
are treated as though the program had a single definition - merging
functions, statics, objects, and anything else as necessary. It is the
programmer's job to ensure that these multiple definitions are identical.)
>
>>
>>> 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:
(The compiler can optimise in many ways - but it is certainly not
allowed to introduce new UB!)
>>
>
> But if the compiler decides to emit an external-linkage definition, it must
> emit it in its entirety, in case another TU wants to call it.
>
Yes - with the usual proviso that with link-time optimisation (or even
just link-time tracking of used functions), it can change that kind of
thing as long as it does not affect the observable behaviour of the program.
> Internal-linkage partial copies shouldn't be problematic. In the end, it's
> more or less what C does, with the exception that C++ allows static.
You can't mix C and C++ like that - C and C++ handle "inline" in
significantly different ways. C++ inline is /not/ just "C inline with
slight modifications".
>
> It was curious to me why C didn't require so much from inline (it's allowed
> to provide a different definition in each TU; it's just unspecified which one
> will be called), and C++ did require an exact copy everywhere. The only
> important difference is that C++ allows static, which may be the reason why.
>
The C++ idea of "inline" requires a much smarter linker - the linker has
to be able to merge multiple definitions of the same object or function
from different places. This is necessary anyway in C++, in order to
handle templates. In the early days of C++, this involved a fair bit of
extra complications with additional programs that collect data about
inline functions and templates that were used, and presenting the linker
with only a single copy - modern linkers are smart enough to handle this
themselves, which simplifies the process. For the C version of
"inline", the semantics and constraints were designed specifically to
support the use of "traditional" linkers without needing any extra
capabilities.
> Hi Thiago,
>
> On 5/5/23 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.
>
> Actually, I'm very confused about static within inline in C, since it seems to
> really be UB in one paragraph, and defined behavior a few paragraphs below:
>
> <https://port70.net/~nsz/c/c11/n1570.html#6.7.4p3>
> 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.
>
> From the above, it seems UB. But then paragraph 7 has a note:
>
> <https://port70.net/~nsz/c/c11/n1570.html#6.7.4p7>
> [...] It is unspecified whether a call to the function uses
> the inline definition or the external definition. 140)
>
> OK so far. Here's the note:
>
> <https://port70.net/~nsz/c/c11/n1570.html#note140>
> Since an inline definition is distinct from the corresponding
> external definition and from any other corresponding inline
> definitions in other translation units, all corresponding
> objects with static storage duration are also distinct in each
> of the definitions.
>
> What? Now the behavior seems to be defined to be distinct in all definitions
> instead of the "shall not" happen from 6.7.4p3. Still unspecified which one
> gets called.
You cannot have two definitions visible at the same time in a single
translation unit. If you have an inline definition of the function, you
cannot have a distinct external definition. You can write an inline
definition, and then an extern declaration for the function (without a
function body) - that makes the same body definition available as an
extern non-inlined function. Alternatively, you can have a non-inline
version defined in a different translation unit. In this case, the
function definitions can be different, and /that/ version's body can use
statics like a normal function.
It is always unspecified which one gets called - the currently available
inline version, or the non-inlined external definition.
(In case anyone has missed it, this is all about C, not C++. The
semantics of C++ "inline" are, IMHO, much simpler - if a function or
variable is declared "inline", it means there can be multiple
definitions of the same function or object (including initialisation) in
the program, and it is the compiler and linker's job to ensure that they
are treated as though the program had a single definition - merging
functions, statics, objects, and anything else as necessary. It is the
programmer's job to ensure that these multiple definitions are identical.)
>
>>
>>> 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:
(The compiler can optimise in many ways - but it is certainly not
allowed to introduce new UB!)
>>
>
> But if the compiler decides to emit an external-linkage definition, it must
> emit it in its entirety, in case another TU wants to call it.
>
Yes - with the usual proviso that with link-time optimisation (or even
just link-time tracking of used functions), it can change that kind of
thing as long as it does not affect the observable behaviour of the program.
> Internal-linkage partial copies shouldn't be problematic. In the end, it's
> more or less what C does, with the exception that C++ allows static.
You can't mix C and C++ like that - C and C++ handle "inline" in
significantly different ways. C++ inline is /not/ just "C inline with
slight modifications".
>
> It was curious to me why C didn't require so much from inline (it's allowed
> to provide a different definition in each TU; it's just unspecified which one
> will be called), and C++ did require an exact copy everywhere. The only
> important difference is that C++ allows static, which may be the reason why.
>
The C++ idea of "inline" requires a much smarter linker - the linker has
to be able to merge multiple definitions of the same object or function
from different places. This is necessary anyway in C++, in order to
handle templates. In the early days of C++, this involved a fair bit of
extra complications with additional programs that collect data about
inline functions and templates that were used, and presenting the linker
with only a single copy - modern linkers are smart enough to handle this
themselves, which simplifies the process. For the C version of
"inline", the semantics and constraints were designed specifically to
support the use of "traditional" linkers without needing any extra
capabilities.
Received on 2023-05-05 15:49:16