Date: Sun, 26 Jan 2025 12:37:12 -0500
On Sun, Jan 26, 2025 at 11:43 AM Thiago Macieira via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> On Friday 24 January 2025 14:45:58 Pacific Standard Time Arthur O'Dwyer
> via
> Std-Proposals wrote:
> > (3) more stable in case this is a long-lived
> > shared-library ABI boundary.
>
> That is a non-goal. If the standard libraries change the mangling, it's
> usually for a good reason and it's going to be something you *do* want to
> have
> reflected in your ABI. Trying to force a name such that mangled names
> remain
> while the structure changed incompatibly is going to lead to
> hard-to-diagnose
> bugs.
>
Let (A) be the client-programmer code:
using MyVec = std::vector<int>;
and (B) be the client-programmer code:
struct MyVec; [...] struct MyVec : std::vector<int> {};
Let some client-programmer code using these definitions be compiled into
two different TUs — "library.so" which is then fixed/frozen/unmodifiable,
and "user.cpp", which will be recompiled later.
Then, let (1) be the scenario where the STL vendor changes the mangling of
`std::vector` without changing its behavior (e.g. by adding or removing a
nested `inline namespace`).
Let (2) be the scenario where the STL vendor changes the behavior of
`std::vector` without changing its mangling (e.g. by replacing
pointer+length with pointer+pointer, or vice versa).
In situation (A+1), we get a linker error, because `MyVec` in
"user.cpp" now mangles differently from how it did at the time "library.so"
was compiled. We cannot fix it except by reintroducing the old mangling for
`std::vector` into "user.cpp".
In situation (A+2), we get UB at runtime. We cannot fix it except by
reintroducing the old behavior into "user.cpp" under the name `std::vector`.
In situation (A+1+2), we get a linker error. We cannot fix it except by
reintroducing the old behavior into "user.cpp" under the old mangling for
`std::vector`.
In situation (B+1), everything works fine.
In situation (B+2), we get UB at runtime. We can fix it by eliminating
`MyVec`'s inheritance from `std::vector` and reimplementing it, in
"user.cpp", according to its old behavior.
In situation (B+1+2), we get UB at runtime. We can fix it by eliminating
`MyVec`'s inheritance from `std::vector` and reimplementing it, in
"user.cpp", according to its old behavior.
The fixes in cases (A+1) and (A+2) involve the client-programmer messing
around with namespace `std`.
The fix in case (B+2) involves the client-programmer messing around only
with the definition of their own `MyVec`.
So I think approach (B) has certain advantages in practice. It's not *clearly
better in every way* — in particular, the build system will flag the
problem in situation (A+1+2) but cause silent UB in situation (B+1+2). But
if you *already know* that you're in situation (1+2) and are tasked with
fixing it, I think I'd rather start from (B+1+2) than from (A+1+2).
my $.02,
–Arthur
std-proposals_at_[hidden]> wrote:
> On Friday 24 January 2025 14:45:58 Pacific Standard Time Arthur O'Dwyer
> via
> Std-Proposals wrote:
> > (3) more stable in case this is a long-lived
> > shared-library ABI boundary.
>
> That is a non-goal. If the standard libraries change the mangling, it's
> usually for a good reason and it's going to be something you *do* want to
> have
> reflected in your ABI. Trying to force a name such that mangled names
> remain
> while the structure changed incompatibly is going to lead to
> hard-to-diagnose
> bugs.
>
Let (A) be the client-programmer code:
using MyVec = std::vector<int>;
and (B) be the client-programmer code:
struct MyVec; [...] struct MyVec : std::vector<int> {};
Let some client-programmer code using these definitions be compiled into
two different TUs — "library.so" which is then fixed/frozen/unmodifiable,
and "user.cpp", which will be recompiled later.
Then, let (1) be the scenario where the STL vendor changes the mangling of
`std::vector` without changing its behavior (e.g. by adding or removing a
nested `inline namespace`).
Let (2) be the scenario where the STL vendor changes the behavior of
`std::vector` without changing its mangling (e.g. by replacing
pointer+length with pointer+pointer, or vice versa).
In situation (A+1), we get a linker error, because `MyVec` in
"user.cpp" now mangles differently from how it did at the time "library.so"
was compiled. We cannot fix it except by reintroducing the old mangling for
`std::vector` into "user.cpp".
In situation (A+2), we get UB at runtime. We cannot fix it except by
reintroducing the old behavior into "user.cpp" under the name `std::vector`.
In situation (A+1+2), we get a linker error. We cannot fix it except by
reintroducing the old behavior into "user.cpp" under the old mangling for
`std::vector`.
In situation (B+1), everything works fine.
In situation (B+2), we get UB at runtime. We can fix it by eliminating
`MyVec`'s inheritance from `std::vector` and reimplementing it, in
"user.cpp", according to its old behavior.
In situation (B+1+2), we get UB at runtime. We can fix it by eliminating
`MyVec`'s inheritance from `std::vector` and reimplementing it, in
"user.cpp", according to its old behavior.
The fixes in cases (A+1) and (A+2) involve the client-programmer messing
around with namespace `std`.
The fix in case (B+2) involves the client-programmer messing around only
with the definition of their own `MyVec`.
So I think approach (B) has certain advantages in practice. It's not *clearly
better in every way* — in particular, the build system will flag the
problem in situation (A+1+2) but cause silent UB in situation (B+1+2). But
if you *already know* that you're in situation (1+2) and are tasked with
fixing it, I think I'd rather start from (B+1+2) than from (A+1+2).
my $.02,
–Arthur
Received on 2025-01-26 17:37:25