C++ Logo

std-discussion

Advanced search

Re: Guarantees over addresses from function pointers created from lambda

From: Federico Kircheis <federico_at_[hidden]>
Date: Sat, 26 Apr 2025 20:24:27 +0200
On 26/04/2025 7:34 pm, Andrey Semashev via Std-Discussion wrote:
> On 26 Apr 2025 20:17, Nate Eldredge via Std-Discussion wrote:
>>
>>
>>> On Apr 26, 2025, at 10:22, Federico Kircheis via Std-Discussion <std-
>>> discussion_at_[hidden]> wrote:
>>>
>>> On 26/04/2025 4:05 pm, mauro russo wrote:
>>>>
>>>
>>>>> * if different function can have the same address, the class if
>>>>> broken, (the lifetime of the previous object did not end correctly)
>>>> This should be something never happening. It cannot make sense
>>>> to have different functions at the same address.
>>>
>>> IMHO it makes sense that
>>>
>>> void foo(){}
>>> void bar(){}
>>>
>>> can have the same address, similarly to how identical string literals
>>> can have the same address; I was not sure if such optimization was
>>> allowed.
>>
>> I think it's forbidden by expr.eq p3.2-3.3 (https://eel.is/c++draft/
>> expr.eq#3.2 <https://eel.is/c++draft/expr.eq#3.2>). Two function
>> pointers compare equal if and only if they "point to the same function".
>> I think it's clear that foo() and bar() are not "the same function",
>> even if they perform the same behavior.


I guess its clear if being "the same function" means having the same
function signature, in the same namespace, and name.
But I am not able to find what "the same function" actually means.
Maybe I'm too pedantic?
What about functions that are declared in C++ and implemented in another
language? For example, with the linker it is possible to create aliases:

For example:

extern "C"{
void foo();
void bar();
}

// in cpp file
void foo() __attribute__((alias("bar")));

I would expect those to have the same address, even if the names or even
namespaces are different.

>> String literals have an explicit exemption from this rule per
>> intro.object p9 (https://eel.is/c++draft/intro.object#9 <https://eel.is/
>> c++draft/intro.object#9>).
>>
>> Somewhat related, clang does a "controversial" optimization where, if a
>> function would unconditionally cause UB if called, the compiler emits a
>> label for it but no code, not even a return instruction. As such, the
>> next function compiled can end up at the same address. See https://
>> godbolt.org/z/775s6e4vv for an example.
>> This does seem like a clear violation of expr.eq p3, but it's
>> been unfixed for a long time.

Oh, that's bad since the function with UB is never called...
Strangely removing volatile helps.

>> So if the situation for lambdas is different, it must be something specific to them.


Ignoring what I wrote about function and the meaning of "the same
function", I guess a lambda with static operator() could be implemented
by the compiler as a wrapper to a function pointer, and multiple lambda
(if the body is the same) would point to the same function.
At least I currently see nothing that prohibits such
implementation/behavior.

> While the standard is pretty clear that two pointers to a function shall
> only compare equal if they point to the same function, the rule has been
> violated by compilers rather often. In both ways. For example, two
> pointers to the same function may compare unequal if the function ends
> up compiled in different shared objects with local visibility (e.g. if
> the function is defined in a header that gets included in both shared
> objects during compilation). And some compilers (MSVC, possibly others)
> also implement an optimization to merge equivalent bodies of different
> functions in the compiled binary, thus reducing the binary size. This
> would result in addresses of foo and bar defined above to compare equal.

I could remember something with msvc, but was not sure anymore and since
I was not able to create an example, I thought I misremembered.
Since from the standard I could not find a clear answer, here I am.

I think it cannot be avoided that a function has multiple addresses when
using shared libraries.
Since those are not specified, it is obviously a grey area.

I was more concerned about different functions having the same address,
especially if there is no way to opt-out from this behavior (which would
be compiler specific).

> The point is, even if the standard guarantees this, it's probably not a
> good idea to rely on this in practice.

So it seems my concerns are not too far-fetched, even without talking
about function pointer created by lambdas:

  * function bodies are not identical
  * intermediate (and valid) compiler optimization makes function body
identical (removes dependency on T)
  * linker optimization (valid or not, happens in practice) merges two
functions with the same behavior together
  * code that relies on equality/inequality misbehaves

Damn.

Received on 2025-04-26 18:24:40