Date: Mon, 02 Jun 2025 10:29:22 -0700
> On Jun 2, 2025, at 7:43 AM, Frederick Virchanza Gotham via Std-Proposals <std-proposals_at_[hidden]> wrote:
>
>
> 99% of C++ compilers make this system very simple
It could be 6 nines, but if your position is “I don’t like the ABI used for C++ polymorphism on windows and therefore it should be ignored” is DoA, as you’re arguing with reality.
> -- any polymorphic
> object will always have a pointer to its polymorphic facilitator
> located at address [base + 0x00] inside the object. What this means,
> is that when we have a "void*", we _can_ actually do the following two
> things even though the Standard doesn't let us to:
You are correct, the standard does not let you do this. That you want to is irrelevant.
> (1) Get the most-derived object (i.e. dynamic_cast to void*)
> (2) Get the type_info
>
> Here are these two features coded up on GodBolt:
>
> https://godbolt.org/z/ajcnb8qda
>
> The above GodBolt works on every C++ compiler ever made. Except for
> one. Microsoft.
>
Incorrect, this fails on Darwin - the process is terminated the moment you try to use the vtable of a non-Dummy typed object as a Dummy. This is not a recoverable error.
> On Microsoft it will work properly the vast majority of the time, but
> sometimes it will crash because of the following fact:
> "Where as 99% of compilers have a uniform way of mapping
> an object to its polymorphic facilitator, the Microsoft
> compiler does not have a uniform way -- it can differ by type."
>
> To bring that a bit more down to Earth:
>
> "The Microsoft compiler doesn't always place the
> vtable pointer at the very beginning of the object."
No, you are assuming the layout of the polymorphic facilitator is such that unrelated types can be used interchangeably, which is UB.
Your code is incorrect the moment you static_cast AnythingAtAll* to Dummy*, I’m surprised none of the sanitizers are tripped by this.
> and here's an example in the following GodBolt:
>
> https://godbolt.org/z/44j7or1rG
Alas godbolt does not run on hardware that supports pointer auth (or the related , but we can set the appropriate flags:
https://godbolt.org/z/4Gb14srK5
Then in the body of GetTypeInfo use can see the `autda` that fails and triggers process termination
>
> The above GodBolt shows that the Microsoft compiler places the vtable
> pointer _after_ the non-polymorphic base, specifically at [base +
> 0x08].
So?
Your code has broken because you decided to try and treat the layout of two unrelated objects as interchangeable. That’s simply incorrect.
>
> So it's because of the Microsoft compiler -- and _only_ because of the
> Microsoft compiler -- that we can't do the two things I talk about
> above. But last night I figured out a possible solution to this
> conundrum.
No, it’s because that is not how the language works, and you are writing code that is wrong. Also again, this fails on darwin, and it will fail on linux once linux adopts pointer authentication. I suspect it would also fail on a hypothetical CHERI system for similar reasons.
> Here comes the fun part, the reverse-engineering -- which is necessary because of the
> burden Microsoft has placed on the C++ standardisation process.
No, MS has not placed a burden here, as you are demanding the standard specify exact behavior of a language implementation detail that is not described anywhere in the standard.
And again, your constant “this works everywhere else” is wrong.
There is a reason why what you are doing is UB, and is precisely because the false assumptions in your code are not sound.
What you are currently asking for is:
1. I want to be able to say “use this ABI instead of that ABI for this class”
2. Then I want the standard to make it not defined behavior to cast and use a polymorphic object of one type as an object of an unrelated type. That means type aliasing optimizations cease to become valid,
(1) is (in principle) easy: that could be a vendor attribute, just as things like “fastcall”, regparam, etc are today
(2) just breaks: the fact the an object of one type cannot be used as an object of an unrelated type is a fairly fundamental feature of C and C++.
What you should be asking for is not “I want an ability to override the polymorphism implementation, because then I can use definitionally erroneous code that happens to work on my own platform, to achieve what I want”. You have fixated on your specific implementation, and have correctly identified that your reliance on UB breaks it other platforms. Rather than asking for language changes to support your incorrect code, you should be asking for language features to support what you are actually trying to do. Not all such language features are possible.
So what is it you are actually trying to do?
—Oliver
>
>
> 99% of C++ compilers make this system very simple
It could be 6 nines, but if your position is “I don’t like the ABI used for C++ polymorphism on windows and therefore it should be ignored” is DoA, as you’re arguing with reality.
> -- any polymorphic
> object will always have a pointer to its polymorphic facilitator
> located at address [base + 0x00] inside the object. What this means,
> is that when we have a "void*", we _can_ actually do the following two
> things even though the Standard doesn't let us to:
You are correct, the standard does not let you do this. That you want to is irrelevant.
> (1) Get the most-derived object (i.e. dynamic_cast to void*)
> (2) Get the type_info
>
> Here are these two features coded up on GodBolt:
>
> https://godbolt.org/z/ajcnb8qda
>
> The above GodBolt works on every C++ compiler ever made. Except for
> one. Microsoft.
>
Incorrect, this fails on Darwin - the process is terminated the moment you try to use the vtable of a non-Dummy typed object as a Dummy. This is not a recoverable error.
> On Microsoft it will work properly the vast majority of the time, but
> sometimes it will crash because of the following fact:
> "Where as 99% of compilers have a uniform way of mapping
> an object to its polymorphic facilitator, the Microsoft
> compiler does not have a uniform way -- it can differ by type."
>
> To bring that a bit more down to Earth:
>
> "The Microsoft compiler doesn't always place the
> vtable pointer at the very beginning of the object."
No, you are assuming the layout of the polymorphic facilitator is such that unrelated types can be used interchangeably, which is UB.
Your code is incorrect the moment you static_cast AnythingAtAll* to Dummy*, I’m surprised none of the sanitizers are tripped by this.
> and here's an example in the following GodBolt:
>
> https://godbolt.org/z/44j7or1rG
Alas godbolt does not run on hardware that supports pointer auth (or the related , but we can set the appropriate flags:
https://godbolt.org/z/4Gb14srK5
Then in the body of GetTypeInfo use can see the `autda` that fails and triggers process termination
>
> The above GodBolt shows that the Microsoft compiler places the vtable
> pointer _after_ the non-polymorphic base, specifically at [base +
> 0x08].
So?
Your code has broken because you decided to try and treat the layout of two unrelated objects as interchangeable. That’s simply incorrect.
>
> So it's because of the Microsoft compiler -- and _only_ because of the
> Microsoft compiler -- that we can't do the two things I talk about
> above. But last night I figured out a possible solution to this
> conundrum.
No, it’s because that is not how the language works, and you are writing code that is wrong. Also again, this fails on darwin, and it will fail on linux once linux adopts pointer authentication. I suspect it would also fail on a hypothetical CHERI system for similar reasons.
> Here comes the fun part, the reverse-engineering -- which is necessary because of the
> burden Microsoft has placed on the C++ standardisation process.
No, MS has not placed a burden here, as you are demanding the standard specify exact behavior of a language implementation detail that is not described anywhere in the standard.
And again, your constant “this works everywhere else” is wrong.
There is a reason why what you are doing is UB, and is precisely because the false assumptions in your code are not sound.
What you are currently asking for is:
1. I want to be able to say “use this ABI instead of that ABI for this class”
2. Then I want the standard to make it not defined behavior to cast and use a polymorphic object of one type as an object of an unrelated type. That means type aliasing optimizations cease to become valid,
(1) is (in principle) easy: that could be a vendor attribute, just as things like “fastcall”, regparam, etc are today
(2) just breaks: the fact the an object of one type cannot be used as an object of an unrelated type is a fairly fundamental feature of C and C++.
What you should be asking for is not “I want an ability to override the polymorphism implementation, because then I can use definitionally erroneous code that happens to work on my own platform, to achieve what I want”. You have fixated on your specific implementation, and have correctly identified that your reliance on UB breaks it other platforms. Rather than asking for language changes to support your incorrect code, you should be asking for language features to support what you are actually trying to do. Not all such language features are possible.
So what is it you are actually trying to do?
—Oliver
Received on 2025-06-02 17:29:39