Date: Wed, 30 Apr 2025 10:29:21 +0200
In the post I was thinking about how it could be generally handled at _runtime_, too, to separate function addresses and identity. And do it also for other pointers to read-only objects. (I do not say const objects as const can be cast away under certain conditions.)
Besides probably needing more memory per pointer or problems with ABI for pointer identification (would it be some kind of RTTI?), which are all big hurdles.
-----Ursprüngliche Nachricht-----
Von:Tiago Freire <tmiguelf_at_[hidden]>
Gesendet:Mi 30.04.2025 09:59
Betreff:RE: [std-proposals] Guarantees over addresses from function pointers created from lambda
An:std-proposals_at_[hidden];
CC:Sebastian Wittmeier <wittmeier_at_[hidden]>;
It’s a good question, probably one that I don’t have a good answer to right now.
I don’t think changing pointers is the way to go.
Maybe what it needs is some form of “shadow data” and a way to use it, stuff that the compiler knows when evaluating things at compile time but that doesn’t translate into a tangible thing at runtime. That is effectively what compilers must do in order to be able to handle addresses (not just function addresses) in a compile time context. How else could you reason comparing function addresses at compile time when there are no addresses.
Somewhat related to the point, take the following example:
constexpr auto f1 = &f;
constexpr auto f2 = &g;
bool test1 = (f1 < f2); //it works its well defined, even if the result is unpredictable
constexpr bool test2 = (f1 < f2); // How does this make sense?
What we are seeing here is the language having evolved to have many different features that individually were useful, but then the combination does not work very well, and ends up with these scars and imperfections.
I think we need to step back and try to redefine what it means to have an address at compile time, or what is happening when we do compile time things.
What that looks like, I don’t know.
From: Std-Proposals <std-proposals-bounces_at_[hidden]> On Behalf Of Sebastian Wittmeier via Std-Proposals
Sent: Wednesday, April 30, 2025 7:41 AM
To: std-proposals_at_[hidden]
Cc: Sebastian Wittmeier <wittmeier_at_[hidden]>
Subject: Re: [std-proposals] Guarantees over addresses from function pointers created from lambda
How to possibly solve it?
We have pointers to the same object with different identity.
Either double indirection of function pointers (as data or code) or additional bits or bytes in the pointer, which are only used for comparison.
-----Ursprüngliche Nachricht-----
Von: Sebastian Wittmeier <wittmeier_at_[hidden] <mailto:wittmeier_at_[hidden]> >
Gesendet: Mi 30.04.2025 07:36
Betreff: AW: [std-proposals] Guarantees over addresses from function pointers created from lambda
An: std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]> ;
Perhaps for functions we have to separate address and identity.
But functions should be first-class citizens.
Separate address and identity for all or at least all 'read-only' objects?
That would also solve some cases with string literals and possibly constinit objects.
-----Ursprüngliche Nachricht-----
Von: Tiago Freire via Std-Proposals <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]> >
Gesendet: Mi 30.04.2025 07:30
Betreff: Re: [std-proposals] Guarantees over addresses from function pointers created from lambda
An: std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]> ; Brian Bi <bbi5291_at_[hidden] <mailto:bbi5291_at_[hidden]> >;
CC: Tiago Freire <tmiguelf_at_[hidden] <mailto:tmiguelf_at_[hidden]> >;
I feel that function addresses at compile time are kind of broken. They can’t be reasoned the same way as regular values.
If:
constexpr auto fun = &foo;
is allowed and
static_assert(&f != &g);
is allowed and
uintptr_t fun_number = std::bit_cast<uintptr_t>(fun)
is allowed
then why not
constexpr uintptr_t fun_number = std::bit_cast<uintptr_t>(fun)
?
That is weird.
Because functions at compile time can’t have addresses, they have “pseudo-adresses”, it’s all just smoke and mirrors, utterly meaningless.
I would not be bothered by
static_assert( &f != &g); //ok
assert( &f != &g); //trouble town
but that is my opinion.
From: Std-Proposals <std-proposals-bounces_at_[hidden] <mailto:std-proposals-bounces_at_[hidden]> > On Behalf Of Arthur O'Dwyer via Std-Proposals
Sent: Wednesday, April 30, 2025 1:58 AM
To: Brian Bi <bbi5291_at_[hidden] <mailto:bbi5291_at_[hidden]> >
Cc: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden] <mailto:arthur.j.odwyer_at_[hidden]> >; std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]>
Subject: Re: [std-proposals] Guarantees over addresses from function pointers created from lambda
On Mon, Apr 28, 2025 at 1:02PM Brian Bi <bbi5291_at_[hidden] <mailto:bbi5291_at_[hidden]> > wrote:
On Mon, Apr 28, 2025 at 12:56PM Arthur O'Dwyer <arthur.j.odwyer_at_[hidden] <mailto:arthur.j.odwyer_at_[hidden]> > wrote:
On Sun, Apr 27, 2025 at 1:22PM Brian Bi via Std-Proposals <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]> > wrote:
A function is not an object, it is a function.
There's no reason to reason about functions as if they were objects.
In https://github.com/bloomberg/bde/blob/main/groups/bsl/bsltf/bsltf_templatetestfacility.h#L1285
we use the fact that different functions have different addresses to produce 128 unique values of type `TemplateTestFacility::MethodPtr` (which is a typedef to a pointer to member function), which are then used to test e.g. containers that must work with various kinds of element types.
That's not quite right [although see below]. You don't rely on the fact that different functions have different addresses; you rely on the fact that functions with different behavior must necessarily have different addresses.
You use (here):
template <int IDENTIFIER>
int TemplateTestFacility_StubClass::method() {
return IDENTIFIER;
}
static_assert(&method<1> != &method<2>);
Ah, right. But I think the only reason we have to give them different behaviour is that MSVC does the standard-noncompliant merging by default. There are probably codebases somewhere that support only Unix, and use empty bodies, and we shouldn't break those users.
Hm, I'm very unsympathetic to the argument that "people might be relying on function addresses to be distinct and we don't want to break them." I'd like to break them if we could.
But I think we still run into a physical roadblock here:
int f(); int g();
static_assert(f != g); // definitely unequal at compile time, right?
int main() { return (f == g); } // but we want to permit them to be equal at run-time?!
The only way to avoid this inconsistency, IMHO, is to treat function addresses as potentially non-unique [entities], i.e., forbid mangling them into symbol names and forbid comparing them at compile-time. Which I think is too onerous. But I'd love to see that proposal anyway — maybe there would be appetite for it!
Such a proposal would definitely want to explore/discuss the status quo for inherited functions and especially inherited virtuals (thunks), where comparing their addresses is already pretty weird at the codegen level.
–Arthur
-- Std-Proposals mailing list Std-Proposals_at_[hidden] <mailto:Std-Proposals_at_[hidden]> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
Received on 2025-04-30 08:36:10