<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/136685>136685</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
[[msvc::no_unique_address]] differs in layout from MSVC ABI
</td>
</tr>
<tr>
<th>Labels</th>
<td>
new issue
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
Ext3h
</td>
</tr>
</table>
<pre>
Consider the following examples:
```c++
#include <cstddef>
struct S1 {};
struct S2 {};
struct S3 {
[[no_unique_address]] [[msvc::no_unique_address]] S1 s1;
};
struct ST1 {
[[no_unique_address]] [[msvc::no_unique_address]] S3 s1;
S1 s2;
};
struct ST2 {
[[no_unique_address]] [[msvc::no_unique_address]] S3 s1;
S2 s2;
};
struct ST3 {
[[no_unique_address]] [[msvc::no_unique_address]] S3 s1;
[[no_unique_address]] [[msvc::no_unique_address]] S1 s2;
};
struct ST4 {
[[no_unique_address]] [[msvc::no_unique_address]] S3 s1;
[[no_unique_address]] [[msvc::no_unique_address]] S2 s2;
};
static_assert(sizeof(ST1) == 2);
size_t size_st1() {
return sizeof(ST1);
}
size_t offset_st1() {
return offsetof(ST1, s2);
}
#ifdef _MSC_VER
static_assert(sizeof(ST2) == 2);
#else
static_assert(sizeof(ST2) == 1);
#endif
size_t size_st2() {
return sizeof(ST2);
}
size_t offset_st2() {
return offsetof(ST2, s2);
}
#ifdef _MSC_VER
static_assert(sizeof(ST3) == 1);
#else
static_assert(sizeof(ST3) == 2);
#endif
size_t size_st3() {
return sizeof(ST3);
}
size_t offset_st3() {
return offsetof(ST3, s2);
}
static_assert(sizeof(ST4) == 1);
size_t size_st4() {
return sizeof(ST4);
}
size_t offset_st4() {
return offsetof(ST4, s2);
}
```
Example output: https://godbolt.org/z/xnrT5Tsq1
For `ST4` the behavior of Clang and MSVC matches in all aspects, both compilers optimize to the expected extent.
For `ST1` the layout does match, but actually not for the same reasons. MSVC preserves `ST1::s1` as a unique address because of the unique sibling `ST1::s2`. Clang preserves `ST1::s1` because otherwise `ST1::s2` would alias `ST1::s1::s1`. And actually the output of ***neither*** compiler is optimized as far as you'd expect.
This difference then causes trouble in `ST2`. Clang applies the optimization as strict aliasing rules are applied, but MSVC doesn't.
In `ST3` we can see the opposite behavior. Even though everything is flagged with `[[msvc::no_unique_address]]`, Clang still tries to avoid aliasing of `ST1::s2` and `ST1::s1::s1`. MSVC does not.
See also https://github.com/microsoft/STL/issues/1364 for the epic depending on ABI equality.
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJy0V0tv4joU_jVmczQosRNKFyyAFmmkO5tLNdvKiU-Ir4yd8XHo49dfOQmlD16LjmQpAtvf6yTHiSTSG4s4Y_mC5Xcj2Yba-dn9cxD1qHDqZbZ0lrRCD6FGqJwx7knbDeCz3DYGiYk5S7oxSfpRMr6II5kzLrQtTasQmFiWFJTCion7fgMF35YB1imwmwW7uWNi8e5f_uHfdxOim0jmAACd6IV1j63Vf1p8lEp5JGL5Hcvvhtkt7cooUsxPrVunQOnAc4TwIf1-RvHGGEGjAH5GAN8L-Dvs_Dz7Xwj8o4Dvq-I5G9nfC_GbEE_VQQZdPkoi9IHxKelXdBXj0_VDyvgtMHHHxB1wxm8Pe_QrPgboLhRSxqfdyt6_x9B6C59wDsTvEVxVEYavGAB7mH7FAWgZTXyBi52gUljB46_18vH3_b_njfHjxrhAQ3j91vTDVqt0dSwefimeI34-x8Oviod_UzzipMeL8YiTyZ6KR1yKR1yOR1wVjzgVzxk_2fEoPpvILpnILpvIrjKRHTexPxx76Pv-8ATXhqYNTMyhDqHpzlK-Yny1capwJoyd3zC-emV89Wz9Q_5Af9IeYOU8sEkS6SZJdzIXWMuddh5cBUsj7QakVfBr_XsJWxnKGgm0BWkMSGqwDBR1Fi7UULptow16AtcEvdWvCMF1mPgcV6ICfA5ow_gTd7rnNvLFtQGUQ-rJOuw2gCxDK415AesCVK5_hyC5RfAoyVka9wobj4R-h7QH7holdQSSQELfMGFomFBgKVvC6DUiDrOkCxNfTT5gcDZJxkMi52jeIEON_kkTfoWBJ9caBdJo-QXhgDSGuVUH51FfX-aolvF5PyzqSPT2-60KoA91UNF8JX28vLiW8Rs11GQoxUOtCZSuKvRoS4xkFjofBMG7tjAYq95pfR-EbBqj45oorieTQTsbiSh4XYbeZUzTtwYJpMdhl9oXt6tcrLll_Gav6OfAJrq8EEppgRAHpsaRDod7dQz3O7QQatduasAd-pdQR05NUBm52aCCJx3qCHnlKRpX8uVgk4I2BoLvrDqQO6fVwVgsx5cSx4fmTGnfPMc7erC8RgRpyH1-hnWo22Jcui3jq60uvSNXBcZX64d_GF9pohaJ8VUqJtnbs4GNLkFhE7txlGhhvvgJ-KeVRoeX8UjNhLoVt3KEs_QmywVP8tt0VM-SYooqV0nJJRZFeSPEhN-maTEVlRAiLUd6xhOeJxnnaZpORDrOpjmPqycyT5IKFcsS3EptxsbstrHxjDqFs1RMJtN8ZGSBhrqvBM4tPkE3yziPHw1-Fjf9KNoNsSwxmgIdYIIOZvi8uOIlqL-Zu141dJXKu22f-3zxc9R6MzuTc2QdLj8a7_7DMnzKOprZzfj_AQAA___qQ-q7">