Repository: microsoft/STL
Pull request #4960: Initially use `[[msvc::no_unique_address]]` for some C++23 components
Author: login: frederick-vs-ja
Labels: name: performance
description: Must go faster
Created: 2024-09-17T16:05:34Z | Merged: 2025-03-25T03:23:08Z

Towards #1364.

Applied:
- `in_value_result`
- `out_value_result`
- `mdspan`
- `join_with_view`'s sentinel
- `enumerate_view`'s iterator and sentinel
- `chunk_view`'s iterator
- `slide_view`'s sentinel
- `elements_of`

Not yet applied due to DevCom-10588147 and possibly related issues.
- views, as they have `view_interface` as their empty bases.
- some iterators with empty bases due to conditionally existing `iterator_category`.
- `chunk_by_view`'s iterator, possibly because the non-potentially-overlapping member is listed first.

Not yet applied because it's unclear to me how to reuse padding bytes in `tuple`
- `zip`-family

Not yet applied due to another unclear reasons
- `expected`

Other changes:
- remove `/* [[no_unique_address]] */` for `_Index_type` of `repeat_view`'s iterator because the `_Index_type` never has padding bytes
- move the test coverage added in #3847 to a separated test file
- ~attempt to test NVCC for these changes~ failed and reverted.


Diff:
diff --git a/stl/inc/algorithm b/stl/inc/algorithm
index 0bfef44a07b..0f8e36e6f3d 100644
--- a/stl/inc/algorithm
+++ b/stl/inc/algorithm
@@ -374,8 +374,8 @@ namespace ranges {
 #if _HAS_CXX23
     _EXPORT_STD template <class _In, class _Ty>
     struct in_value_result {
-        /* [[no_unique_address]] */ _In in;
-        /* [[no_unique_address]] */ _Ty value;
+        _MSVC_NO_UNIQUE_ADDRESS _In in;
+        _MSVC_NO_UNIQUE_ADDRESS _Ty value;
 
         template <class _IIn, class _TTy>
             requires convertible_to<const _In&, _IIn> && convertible_to<const _Ty&, _TTy>
diff --git a/stl/inc/mdspan b/stl/inc/mdspan
index bedc6046035..fa151c8a499 100644
--- a/stl/inc/mdspan
+++ b/stl/inc/mdspan
@@ -23,8 +23,8 @@ _STL_DISABLE_CLANG_WARNINGS
 #undef new
 
 // TRANSITION, non-_Ugly attribute tokens
-#pragma push_macro("empty_bases")
-#undef empty_bases
+#pragma push_macro("msvc")
+#undef msvc
 
 _STD_BEGIN
 template <class _IndexType, size_t _Size>
@@ -1073,37 +1073,37 @@ concept _Elidable_layout_mapping =
     || (same_as<_LayoutPolicy, layout_stride> && _Extents::rank() == 0);
 
 template <class _Extents, class _LayoutPolicy>
-struct _Mdspan_mapping_base {
+struct _Mdspan_mapping_holder {
     _STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>);
 
     using _Mapping = _LayoutPolicy::template mapping<_Extents>;
 
-    constexpr _Mdspan_mapping_base() noexcept = default;
+    constexpr _Mdspan_mapping_holder() noexcept = default;
 
-    constexpr explicit _Mdspan_mapping_base(const _Extents& _Exts) : _Map(_Exts) {}
+    constexpr explicit _Mdspan_mapping_holder(const _Extents& _Exts) : _Map(_Exts) {}
 
-    constexpr explicit _Mdspan_mapping_base(_Extents&& _Exts) : _Map(_STD move(_Exts)) {}
+    constexpr explicit _Mdspan_mapping_holder(_Extents&& _Exts) : _Map(_STD move(_Exts)) {}
 
     template <class _OtherMapping>
-    constexpr explicit _Mdspan_mapping_base(const _OtherMapping& _Map_) : _Map(_Map_) {}
+    constexpr explicit _Mdspan_mapping_holder(const _OtherMapping& _Map_) : _Map(_Map_) {}
 
-    _Mapping _Map = _Mapping();
+    _MSVC_NO_UNIQUE_ADDRESS _Mapping _Map = _Mapping();
 };
 
 template <class _Extents, _Elidable_layout_mapping<_Extents> _LayoutPolicy>
-struct _Mdspan_mapping_base<_Extents, _LayoutPolicy> {
+struct _Mdspan_mapping_holder<_Extents, _LayoutPolicy> {
     _STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>);
 
     using _Mapping = _LayoutPolicy::template mapping<_Extents>;
 
-    constexpr _Mdspan_mapping_base() noexcept = default;
+    constexpr _Mdspan_mapping_holder() noexcept = default;
 
-    constexpr explicit _Mdspan_mapping_base(const _Extents&) noexcept {}
+    constexpr explicit _Mdspan_mapping_holder(const _Extents&) noexcept {}
 
-    constexpr explicit _Mdspan_mapping_base(_Extents&&) noexcept {}
+    constexpr explicit _Mdspan_mapping_holder(_Extents&&) noexcept {}
 
     template <class _OtherMapping>
-    constexpr explicit _Mdspan_mapping_base(const _OtherMapping& _Map_) {
+    constexpr explicit _Mdspan_mapping_holder(const _OtherMapping& _Map_) {
         // NB: Constructing _Mapping from _OtherMapping may have side effects - we should create a temporary.
         if constexpr (!_Elidable_layout_mapping<typename _OtherMapping::layout_type, _Extents>) {
             (void) _Mapping{_Map_};
@@ -1117,21 +1117,21 @@ template <class _AccessorPolicy>
 concept _Elidable_accessor_policy = _Is_specialization_v<_AccessorPolicy, default_accessor>;
 
 template <class _AccessorPolicy>
-struct _Mdspan_accessor_base {
-    constexpr _Mdspan_accessor_base() noexcept = default;
+struct _Mdspan_accessor_holder {
+    constexpr _Mdspan_accessor_holder() noexcept = default;
 
     template <class _OtherAccessorPolicy>
-    constexpr explicit _Mdspan_accessor_base(const _OtherAccessorPolicy& _Acc_) : _Acc(_Acc_) {}
+    constexpr explicit _Mdspan_accessor_holder(const _OtherAccessorPolicy& _Acc_) : _Acc(_Acc_) {}
 
-    _AccessorPolicy _Acc = _AccessorPolicy();
+    _MSVC_NO_UNIQUE_ADDRESS _AccessorPolicy _Acc = _AccessorPolicy();
 };
 
 template <_Elidable_accessor_policy _AccessorPolicy>
-struct _Mdspan_accessor_base<_AccessorPolicy> {
-    constexpr _Mdspan_accessor_base() noexcept = default;
+struct _Mdspan_accessor_holder<_AccessorPolicy> {
+    constexpr _Mdspan_accessor_holder() noexcept = default;
 
     template <class _OtherAccessorPolicy>
-    constexpr explicit _Mdspan_accessor_base(const _OtherAccessorPolicy& _Acc_) {
+    constexpr explicit _Mdspan_accessor_holder(const _OtherAccessorPolicy& _Acc_) {
         // NB: Constructing _AccessorPolicy from _OtherAccessorPolicy may have side effects - we should create a
         // temporary.
         if constexpr (!_Elidable_accessor_policy<_OtherAccessorPolicy>) {
@@ -1160,8 +1160,7 @@ _NODISCARD constexpr _IndexType _Mdspan_checked_index_cast(_OtherIndexType&& _Id
 
 _EXPORT_STD template <class _ElementType, class _Extents, class _LayoutPolicy = layout_right,
     class _AccessorPolicy = default_accessor<_ElementType>>
-class __declspec(empty_bases) mdspan : private _Mdspan_mapping_base<_Extents, _LayoutPolicy>,
-                                       private _Mdspan_accessor_base<_AccessorPolicy> {
+class mdspan {
 public:
     using extents_type     = _Extents;
     using layout_type      = _LayoutPolicy;
@@ -1176,9 +1175,6 @@ public:
     using reference        = accessor_type::reference;
 
 private:
-    using _Mapping_base  = _Mdspan_mapping_base<extents_type, layout_type>;
-    using _Accessor_base = _Mdspan_accessor_base<accessor_type>;
-
     static_assert(
         sizeof(element_type) > 0, "ElementType must be a complete type (N4950 [mdspan.mdspan.overview]/2.1).");
     static_assert(
@@ -1208,7 +1204,7 @@ public:
     }
 
     _NODISCARD constexpr index_type extent(_In_range_(<, extents_type::_Rank) const rank_type _Idx) const noexcept {
-        return this->_Map.extents().extent(_Idx);
+        return _Mapping._Map.extents().extent(_Idx);
     }
 
     constexpr mdspan() noexcept(is_nothrow_default_constructible_v<data_handle_type>
@@ -1229,8 +1225,7 @@ public:
     constexpr explicit mdspan(data_handle_type _Ptr_, _OtherIndexTypes... _Exts)
         noexcept(is_nothrow_constructible_v<mapping_type, extents_type>
                  && is_nothrow_default_constructible_v<accessor_type>) // strengthened
-        : _Mapping_base(extents_type{static_cast<index_type>(_STD move(_Exts))...}), _Accessor_base(),
-          _Ptr(_STD move(_Ptr_)) {}
+        : _Mapping(extents_type{static_cast<index_type>(_STD move(_Exts))...}), _Accessor(), _Ptr(_STD move(_Ptr_)) {}
 
     template <class _OtherIndexType, size_t _Size>
         requires is_convertible_v<const _OtherIndexType&, index_type>
@@ -1240,7 +1235,7 @@ public:
     constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_, span<_OtherIndexType, _Size> _Exts)
         noexcept(is_nothrow_constructible_v<mapping_type, extents_type>
                  && is_nothrow_default_constructible_v<accessor_type>) // strengthened
-        : _Mapping_base(extents_type{_Exts}), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {}
+        : _Mapping(extents_type{_Exts}), _Accessor(), _Ptr(_STD move(_Ptr_)) {}
 
     template <class _OtherIndexType, size_t _Size>
         requires is_convertible_v<const _OtherIndexType&, index_type>
@@ -1251,23 +1246,23 @@ public:
         mdspan(data_handle_type _Ptr_, const array<_OtherIndexType, _Size>& _Exts)
             noexcept(is_nothrow_constructible_v<mapping_type, extents_type>
                      && is_nothrow_default_constructible_v<accessor_type>) // strengthened
-        : _Mapping_base(extents_type{_Exts}), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {}
+        : _Mapping(extents_type{_Exts}), _Accessor(), _Ptr(_STD move(_Ptr_)) {}
 
     constexpr mdspan(data_handle_type _Ptr_, const extents_type& _Exts)
         noexcept(is_nothrow_constructible_v<mapping_type, const extents_type&>
                  && is_nothrow_default_constructible_v<accessor_type>) // strengthened
         requires is_constructible_v<mapping_type, const extents_type&> && is_default_constructible_v<accessor_type>
-        : _Mapping_base(_Exts), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {}
+        : _Mapping(_Exts), _Accessor(), _Ptr(_STD move(_Ptr_)) {}
 
     constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_)
         noexcept(is_nothrow_copy_constructible_v<mapping_type>
                  && is_nothrow_default_constructible_v<accessor_type>) // strengthened
         requires is_default_constructible_v<accessor_type>
-        : _Mapping_base(_Map_), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {}
+        : _Mapping(_Map_), _Accessor(), _Ptr(_STD move(_Ptr_)) {}
 
     constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_, const accessor_type& _Acc_) noexcept(
         is_nothrow_copy_constructible_v<mapping_type> && is_nothrow_copy_constructible_v<accessor_type>) // strengthened
-        : _Mapping_base(_Map_), _Accessor_base(_Acc_), _Ptr(_STD move(_Ptr_)) {}
+        : _Mapping(_Map_), _Accessor(_Acc_), _Ptr(_STD move(_Ptr_)) {}
 
     template <class _OtherElementType, class _OtherExtents, class _OtherLayoutPolicy, class _OtherAccessor>
         requires is_constructible_v<mapping_type, const typename _OtherLayoutPolicy::template mapping<_OtherExtents>&>
@@ -1280,7 +1275,7 @@ public:
                      && is_nothrow_constructible_v<mapping_type,
                          const typename _OtherLayoutPolicy::template mapping<_OtherExtents>&>
                      && is_nothrow_constructible_v<accessor_type, const _OtherAccessor&>) // strengthened
-        : _Mapping_base(_Other.mapping()), _Accessor_base(_Other.accessor()), _Ptr(_Other.data_handle()) {
+        : _Mapping(_Other.mapping()), _Accessor(_Other.accessor()), _Ptr(_Other.data_handle()) {
         static_assert(is_constructible_v<data_handle_type, const typename _OtherAccessor::data_handle_type&>,
             "The data_handle_type must be constructible from const typename OtherAccessor::data_handle_type& (N4950 "
             "[mdspan.mdspan.cons]/20.1).");
@@ -1346,20 +1341,21 @@ public:
     _NODISCARD constexpr size_type size() const noexcept {
 #if _ITERATOR_DEBUG_LEVEL != 0
         if constexpr (rank_dynamic() != 0) {
-            _STL_VERIFY(this->_Map.extents().template _Is_dynamic_multidim_index_space_size_representable<size_type>(),
+            _STL_VERIFY(
+                _Mapping._Map.extents().template _Is_dynamic_multidim_index_space_size_representable<size_type>(),
                 "The size of the multidimensional index space extents() must be representable as a value of type "
                 "size_type (N4950 [mdspan.mdspan.members]/7).");
         }
 #endif // _ITERATOR_DEBUG_LEVEL != 0
         return static_cast<size_type>(
-            _Fwd_prod_of_extents<extents_type>::_Calculate(this->_Map.extents(), extents_type::_Rank));
+            _Fwd_prod_of_extents<extents_type>::_Calculate(_Mapping._Map.extents(), extents_type::_Rank));
     }
 
     _NODISCARD constexpr bool empty() const noexcept {
         if constexpr (extents_type::_Multidim_index_space_size_is_always_zero) {
             return true;
         } else {
-            const extents_type& _Exts = this->_Map.extents();
+            const extents_type& _Exts = _Mapping._Map.extents();
             for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) {
                 if (_Exts.extent(_Idx) == 0) {
                     return true;
@@ -1373,16 +1369,16 @@ public:
         swap(_Left._Ptr, _Right._Ptr); // intentional ADL
 
         if constexpr (!_Elidable_layout_mapping<layout_type, extents_type>) {
-            swap(_Left._Map, _Right._Map); // intentional ADL
+            swap(_Left._Mapping._Map, _Right._Mapping._Map); // intentional ADL
         }
 
         if constexpr (!_Elidable_accessor_policy<accessor_type>) {
-            swap(_Left._Acc, _Right._Acc); // intentional ADL
+            swap(_Left._Accessor._Acc, _Right._Accessor._Acc); // intentional ADL
         }
     }
 
     _NODISCARD constexpr const extents_type& extents() const noexcept {
-        return this->_Map.extents();
+        return _Mapping._Map.extents();
     }
 
     _NODISCARD constexpr const data_handle_type& data_handle() const noexcept {
@@ -1390,11 +1386,11 @@ public:
     }
 
     _NODISCARD constexpr const mapping_type& mapping() const noexcept {
-        return this->_Map;
+        return _Mapping._Map;
     }
 
     _NODISCARD constexpr const accessor_type& accessor() const noexcept {
-        return this->_Acc;
+        return _Accessor._Acc;
     }
 
     _NODISCARD static constexpr bool is_always_unique() noexcept /* strengthened */ {
@@ -1412,38 +1408,42 @@ public:
         return _Result;
     }
 
-    _NODISCARD constexpr bool is_unique() const noexcept(noexcept(this->_Map.is_unique())) /* strengthened */ {
-        return this->_Map.is_unique();
+    _NODISCARD constexpr bool is_unique() const noexcept(noexcept(_Mapping._Map.is_unique())) /* strengthened */ {
+        return _Mapping._Map.is_unique();
     }
 
-    _NODISCARD constexpr bool is_exhaustive() const noexcept(noexcept(this->_Map.is_exhaustive())) /* strengthened */ {
-        return this->_Map.is_exhaustive();
+    _NODISCARD constexpr bool is_exhaustive() const
+        noexcept(noexcept(_Mapping._Map.is_exhaustive())) /* strengthened */ {
+        return _Mapping._Map.is_exhaustive();
     }
 
-    _NODISCARD constexpr bool is_strided() const noexcept(noexcept(this->_Map.is_strided())) /* strengthened */ {
-        return this->_Map.is_strided();
+    _NODISCARD constexpr bool is_strided() const noexcept(noexcept(_Mapping._Map.is_strided())) /* strengthened */ {
+        return _Mapping._Map.is_strided();
     }
 
     _NODISCARD constexpr index_type stride(const rank_type _Idx) const
-        noexcept(noexcept(this->_Map.stride(_Idx))) /* strengthened */ {
-        return this->_Map.stride(_Idx);
+        noexcept(noexcept(_Mapping._Map.stride(_Idx))) /* strengthened */ {
+        return _Mapping._Map.stride(_Idx);
     }
 
 private:
     template <class... _OtherIndexTypes>
     _NODISCARD constexpr reference _Access_impl(_OtherIndexTypes... _Indices) const
-        noexcept(noexcept(this->_Acc.access(_Ptr, static_cast<size_t>(this->_Map(_Indices...))))) {
+        noexcept(noexcept(_Accessor._Acc.access(_Ptr, static_cast<size_t>(_Mapping._Map(_Indices...))))) {
         _STL_INTERNAL_STATIC_ASSERT(conjunction_v<is_same<_OtherIndexTypes, index_type>...>);
 #if _MSVC_STL_HARDENING_MDSPAN || _ITERATOR_DEBUG_LEVEL != 0
-        _STL_VERIFY(this->_Map.extents()._Contains_multidimensional_index(make_index_sequence<rank()>{}, _Indices...),
-            "mdspan subscript out of range; extents_type::index-cast(std::move(indices)) must be "
-            "a multidimensional index in extents() (N5001 [mdspan.mdspan.members]/3).");
+        _STL_VERIFY(
+            _Mapping._Map.extents()._Contains_multidimensional_index(make_index_sequence<rank()>{}, _Indices...),
+            "mdspan subscript out of range; extents_type::index-cast(std::move(indices)) must be a multidimensional "
+            "index in extents() (N5001 [mdspan.mdspan.members]/3).");
 #endif
 
-        return this->_Acc.access(_Ptr, static_cast<size_t>(this->_Map(_Indices...)));
+        return _Accessor._Acc.access(_Ptr, static_cast<size_t>(_Mapping._Map(_Indices...)));
     }
 
-    /* [[no_unique_address]] */ data_handle_type _Ptr = data_handle_type();
+    _MSVC_NO_UNIQUE_ADDRESS _Mdspan_mapping_holder<extents_type, layout_type> _Mapping;
+    _MSVC_NO_UNIQUE_ADDRESS _Mdspan_accessor_holder<accessor_type> _Accessor;
+    _MSVC_NO_UNIQUE_ADDRESS data_handle_type _Ptr = data_handle_type();
 };
 
 template <class _CArray>
@@ -1480,7 +1480,7 @@ mdspan(const typename _AccessorType::data_handle_type&, const _MappingType&, con
 _STD_END
 
 // TRANSITION, non-_Ugly attribute tokens
-#pragma pop_macro("empty_bases")
+#pragma pop_macro("msvc")
 
 #pragma pop_macro("new")
 _STL_RESTORE_CLANG_WARNINGS
diff --git a/stl/inc/ranges b/stl/inc/ranges
index 06520461db3..8768eede7ea 100644
--- a/stl/inc/ranges
+++ b/stl/inc/ranges
@@ -31,6 +31,10 @@ _STL_DISABLE_CLANG_WARNINGS
 #pragma push_macro("new")
 #undef new
 
+// TRANSITION, non-_Ugly attribute tokens
+#pragma push_macro("msvc")
+#undef msvc
+
 _STD_BEGIN
 namespace ranges {
     // MUCH machinery defined in <xutility>, some in <__msvc_ranges_to.hpp>
@@ -1087,7 +1091,7 @@ namespace ranges {
             using _Index_type = conditional_t<same_as<_Bo, unreachable_sentinel_t>, ptrdiff_t, _Bo>;
 
             const _Ty* _Value{};
-            /* [[no_unique_address]] */ _Index_type _Current{};
+            _Index_type _Current{};
 
             constexpr explicit _Iterator(const _Ty* _Val, _Index_type _Bo_ = _Index_type{}) noexcept // strengthened
                 : _Value(_Val), _Current(_Bo_) {
@@ -3453,7 +3457,7 @@ namespace ranges {
             using _Parent_t = _Maybe_const<_Const, join_with_view>;
             using _Base     = _Maybe_const<_Const, _Vw>;
 
-            /* [[no_unique_address]] */ sentinel_t<_Base> _Last{};
+            _MSVC_NO_UNIQUE_ADDRESS sentinel_t<_Base> _Last{};
 
             constexpr explicit _Sentinel(_Parent_t& _Parent)
                 noexcept(noexcept(_RANGES end(_Parent._Range))
@@ -5129,7 +5133,7 @@ namespace ranges {
             using _Base_iterator  = iterator_t<_Base_t>;
             using _Reference_type = tuple<range_difference_t<_Base_t>, range_reference_t<_Base_t>>;
 
-            /* [[no_unique_address]] */ _Base_iterator _Current{};
+            _MSVC_NO_UNIQUE_ADDRESS _Base_iterator _Current{};
             range_difference_t<_Base_t> _Pos = 0;
 
             constexpr explicit _Iterator(_Base_iterator _Current_, range_difference_t<_Base_t> _Pos_)
@@ -5289,7 +5293,7 @@ namespace ranges {
             using _Base_t        = _Maybe_const<_Const, _Vw>;
             using _Base_sentinel = sentinel_t<_Base_t>;
 
-            /* [[no_unique_address]] */ _Base_sentinel _End{};
+            _MSVC_NO_UNIQUE_ADDRESS _Base_sentinel _End{};
 
             constexpr explicit _Sentinel(_Base_sentinel _End_)
                 noexcept(is_nothrow_move_constructible_v<_Base_sentinel>) // strengthened
@@ -5725,8 +5729,8 @@ namespace ranges {
             using _Base_iterator = iterator_t<_Base>;
             using _Base_sentinel = sentinel_t<_Base>;
 
-            /* [[no_unique_address]] */ _Base_iterator _Current{};
-            /* [[no_unique_address]] */ _Base_sentinel _End{};
+            _MSVC_NO_UNIQUE_ADDRESS _Base_iterator _Current{};
+            _MSVC_NO_UNIQUE_ADDRESS _Base_sentinel _End{};
             range_difference_t<_Base> _Count   = 0;
             range_difference_t<_Base> _Missing = 0;
 
@@ -6284,7 +6288,7 @@ namespace ranges {
         class _Sentinel {
         private:
             friend slide_view;
-            /* [[no_unique_address]] */ sentinel_t<_Vw> _Last{};
+            _MSVC_NO_UNIQUE_ADDRESS sentinel_t<_Vw> _Last{};
 
             constexpr explicit _Sentinel(sentinel_t<_Vw> _Last_)
                 noexcept(is_nothrow_move_constructible_v<sentinel_t<_Vw>>) /* strengthened */
@@ -9243,6 +9247,9 @@ _EXPORT_STD namespace views = ranges::views;
 
 _STD_END
 
+// TRANSITION, non-_Ugly attribute tokens
+#pragma pop_macro("msvc")
+
 #pragma pop_macro("new")
 _STL_RESTORE_CLANG_WARNINGS
 #pragma warning(pop)
diff --git a/stl/inc/xmemory b/stl/inc/xmemory
index acac82131e1..21cb148dc12 100644
--- a/stl/inc/xmemory
+++ b/stl/inc/xmemory
@@ -25,6 +25,10 @@ _STL_DISABLE_CLANG_WARNINGS
 #pragma push_macro("new")
 #undef new
 
+// TRANSITION, non-_Ugly attribute tokens
+#pragma push_macro("msvc")
+#undef msvc
+
 #if _USE_STD_VECTOR_ALGORITHMS
 extern "C" {
 void* __stdcall __std_remove_1(void* _First, void* _Last, uint8_t _Val) noexcept;
@@ -2736,8 +2740,8 @@ namespace ranges {
     _EXPORT_STD template <range _Rng, class _Alloc>
 #endif // ^^^ !defined(__cpp_lib_byte) ^^^
     struct elements_of {
-        /* [[no_unique_address]] */ _Rng range;
-        /* [[no_unique_address]] */ _Alloc allocator{};
+        _MSVC_NO_UNIQUE_ADDRESS _Rng range;
+        _MSVC_NO_UNIQUE_ADDRESS _Alloc allocator{};
     };
 
 #if defined(__cpp_lib_byte)
@@ -2801,6 +2805,9 @@ _STD_END
 #define _LIST_REMOVE_RETURN void
 #endif // ^^^ !_HAS_CXX20 ^^^
 
+// TRANSITION, non-_Ugly attribute tokens
+#pragma pop_macro("msvc")
+
 #pragma pop_macro("new")
 _STL_RESTORE_CLANG_WARNINGS
 #pragma warning(pop)
diff --git a/stl/inc/xutility b/stl/inc/xutility
index 966a1d66036..e1a0c2fd8e5 100644
--- a/stl/inc/xutility
+++ b/stl/inc/xutility
@@ -4949,8 +4949,8 @@ namespace ranges {
 #if _HAS_CXX23
     _EXPORT_STD template <class _Out, class _Ty>
     struct out_value_result {
-        /* [[no_unique_address]] */ _Out out;
-        /* [[no_unique_address]] */ _Ty value;
+        _MSVC_NO_UNIQUE_ADDRESS _Out out;
+        _MSVC_NO_UNIQUE_ADDRESS _Ty value;
 
         template <_Convertible_from<const _Out&> _OOut, _Convertible_from<const _Ty&> _TTy>
         constexpr operator out_value_result<_OOut, _TTy>() const& {
diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h
index 3525693fa21..3240ccc5b8a 100644
--- a/stl/inc/yvals_core.h
+++ b/stl/inc/yvals_core.h
@@ -739,6 +739,18 @@
 #define _MSVC_LIFETIMEBOUND
 #endif
 
+#if _HAS_CXX23 // TRANSITION, ABI, should just use [[no_unique_address]] when _HAS_CXX20.
+// Should we enable use of [[msvc::no_unique_address]] or [[no_unique_address]] to allow potentially-overlapping member
+// subobjects?
+#if _HAS_MSVC_ATTRIBUTE(no_unique_address)
+#define _MSVC_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
+#elif __has_cpp_attribute(no_unique_address) // TRANSITION, DevCom-10747012, EDG recognizes [[no_unique_address]].
+#define _MSVC_NO_UNIQUE_ADDRESS [[no_unique_address]]
+#else
+#error Either [[msvc::no_unique_address]] or [[no_unique_address]] must be supported because this is ABI-critical.
+#endif
+#endif // _HAS_CXX23
+
 #undef _HAS_MSVC_ATTRIBUTE
 #pragma pop_macro("lifetimebound")
 #pragma pop_macro("intrinsic")
diff --git a/tests/std/include/test_mdspan_support.hpp b/tests/std/include/test_mdspan_support.hpp
index 500d22be37a..e9706697bed 100644
--- a/tests/std/include/test_mdspan_support.hpp
+++ b/tests/std/include/test_mdspan_support.hpp
@@ -175,6 +175,28 @@ constexpr bool check_accessor_policy_requirements() {
     return true;
 }
 
+template <class ElementType>
+struct TrivialAccessor {
+    using offset_policy    = TrivialAccessor;
+    using element_type     = ElementType;
+    using reference        = ElementType&;
+    using data_handle_type = ElementType*;
+
+    constexpr reference access(data_handle_type handle, std::size_t off) const noexcept {
+        return handle[off];
+    }
+
+    constexpr data_handle_type offset(data_handle_type handle, std::size_t off) const noexcept {
+        return handle + off;
+    }
+
+    int member;
+};
+
+static_assert(check_accessor_policy_requirements<TrivialAccessor<int>>());
+static_assert(std::is_trivially_copyable_v<TrivialAccessor<int>>);
+static_assert(std::is_trivially_default_constructible_v<TrivialAccessor<int>>);
+
 namespace detail {
     template <size_t... Extents, class Fn>
     constexpr void check_members_with_mixed_extents(Fn&& fn) {
diff --git a/tests/std/test.lst b/tests/std/test.lst
index e188823bfcf..c6238c7781f 100644
--- a/tests/std/test.lst
+++ b/tests/std/test.lst
@@ -189,6 +189,7 @@ tests\GH_001103_countl_zero_correctness
 tests\GH_001105_custom_streambuf_throws
 tests\GH_001123_random_cast_out_of_range
 tests\GH_001277_num_get_bad_grouping
+tests\GH_001394_msvc_no_unique_address_23
 tests\GH_001411_core_headers
 tests\GH_001530_binomial_accuracy
 tests\GH_001541_case_sensitive_boolalpha
@@ -279,6 +280,7 @@ tests\P0009R18_mdspan_layout_stride
 tests\P0009R18_mdspan_layout_stride_death
 tests\P0009R18_mdspan_mdspan
 tests\P0009R18_mdspan_mdspan_death
+tests\P0009R18_mdspan_msabi
 tests\P0019R8_atomic_ref
 tests\P0024R2_parallel_algorithms_adjacent_difference
 tests\P0024R2_parallel_algorithms_adjacent_find
diff --git a/tests/std/tests/GH_001394_msvc_no_unique_address_23/env.lst b/tests/std/tests/GH_001394_msvc_no_unique_address_23/env.lst
new file mode 100644
index 00000000000..642f530ffad
--- /dev/null
+++ b/tests/std/tests/GH_001394_msvc_no_unique_address_23/env.lst
@@ -0,0 +1,4 @@
+# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+RUNALL_INCLUDE ..\usual_latest_matrix.lst
diff --git a/tests/std/tests/GH_001394_msvc_no_unique_address_23/test.compile.pass.cpp b/tests/std/tests/GH_001394_msvc_no_unique_address_23/test.compile.pass.cpp
new file mode 100644
index 00000000000..a9bf3b07850
--- /dev/null
+++ b/tests/std/tests/GH_001394_msvc_no_unique_address_23/test.compile.pass.cpp
@@ -0,0 +1,102 @@
+// Copyright (c) Microsoft Corporation.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+// Tests MSVC STL specific behavior on ABI.
+
+#include <algorithm>
+#include <cstddef>
+#include <functional>
+#include <memory>
+#include <memory_resource>
+#include <ranges>
+#include <type_traits>
+
+using namespace std;
+
+static_assert(sizeof(ranges::in_value_result<ranges::dangling, int>) == sizeof(int));
+static_assert(sizeof(ranges::in_value_result<int, ranges::dangling>) == sizeof(int));
+#ifndef __EDG__ // TRANSITION, DevCom-10747012
+static_assert(is_empty_v<ranges::in_value_result<ranges::dangling, ranges::dangling>>);
+#endif // ^^^ no workaround ^^^
+
+static_assert(sizeof(ranges::out_value_result<ranges::dangling, int>) == sizeof(int));
+static_assert(sizeof(ranges::out_value_result<int, ranges::dangling>) == sizeof(int));
+#ifndef __EDG__ // TRANSITION, DevCom-10747012
+static_assert(is_empty_v<ranges::out_value_result<ranges::dangling, ranges::dangling>>);
+#endif // ^^^ no workaround ^^^
+
+struct stateless_input_it {
+    using value_type      = int;
+    using difference_type = ptrdiff_t;
+
+    int operator*() const noexcept {
+        return 42;
+    }
+
+    stateless_input_it& operator++() noexcept {
+        return *this;
+    }
+
+    stateless_input_it operator++(int) noexcept {
+        return {};
+    }
+};
+
+struct stateless_forward_it {
+    using value_type      = int;
+    using difference_type = ptrdiff_t;
+
+    int operator*() const noexcept {
+        return 42;
+    }
+
+    stateless_forward_it& operator++() noexcept {
+        return *this;
+    }
+
+    stateless_forward_it operator++(int) noexcept {
+        return {};
+    }
+
+    friend bool operator==(stateless_forward_it, stateless_forward_it) = default;
+};
+
+static_assert(input_iterator<stateless_input_it>);
+static_assert(forward_iterator<stateless_forward_it>);
+
+using test_range                 = ranges::subrange<int*, unreachable_sentinel_t>;
+using test_outer_range           = ranges::subrange<const test_range*, unreachable_sentinel_t>;
+using stateless_input_range      = ranges::subrange<stateless_input_it, unreachable_sentinel_t>;
+using stateless_fwd_range        = ranges::subrange<stateless_forward_it, unreachable_sentinel_t>;
+using stateless_fwd_common_range = ranges::subrange<stateless_forward_it, stateless_forward_it>;
+
+#ifndef __EDG__ // TRANSITION, DevCom-10747012
+static_assert(is_empty_v<decltype((test_outer_range{} | views::join_with(test_range{})).end())>);
+#endif // ^^^ no workaround ^^^
+
+static_assert(sizeof((stateless_input_range{} | views::enumerate).begin()) == sizeof(ptrdiff_t));
+#ifndef __EDG__ // TRANSITION, DevCom-10747012
+static_assert(is_empty_v<decltype((stateless_input_range{} | views::enumerate).end())>);
+#endif // ^^^ no workaround ^^^
+
+static_assert(sizeof((stateless_fwd_range{} | views::chunk(42)).begin()) == sizeof(ptrdiff_t) * 2);
+#ifndef __EDG__ // TRANSITION, DevCom-10747012
+static_assert(sizeof((stateless_fwd_common_range{} | views::chunk(42)).begin()) == sizeof(ptrdiff_t) * 2);
+#endif // ^^^ no workaround ^^^
+
+static_assert(sizeof((stateless_fwd_range{} | views::chunk_by(ranges::less{})).begin()) == sizeof(void*) * 2);
+static_assert(sizeof((stateless_fwd_common_range{} | views::chunk_by(ranges::less{})).begin()) == sizeof(void*) * 2);
+
+#ifndef __EDG__ // TRANSITION, DevCom-10747012
+static_assert(is_empty_v<decltype((test_range{} | views::slide(42)).end())>);
+#endif // ^^^ no workaround ^^^
+
+static_assert(sizeof(ranges::elements_of<int[1], allocator<int>>) == sizeof(int[1]));
+static_assert(sizeof(ranges::elements_of<int (&)[1], allocator<int>>) == sizeof(int (*)[1]));
+static_assert(sizeof(ranges::elements_of<ranges::empty_view<int>, pmr::polymorphic_allocator<int>>)
+              == sizeof(pmr::polymorphic_allocator<int>));
+static_assert(sizeof(ranges::elements_of<ranges::empty_view<int>, allocator<int>>) == 1);
+
+#ifndef __EDG__ // TRANSITION, DevCom-10747012
+static_assert(is_empty_v<ranges::elements_of<ranges::empty_view<int>, allocator<int>>>);
+#endif // ^^^ no workaround ^^^
diff --git a/tests/std/tests/P0009R18_mdspan_extents/test.cpp b/tests/std/tests/P0009R18_mdspan_extents/test.cpp
index 2dedead9cc9..90163bfe391 100644
--- a/tests/std/tests/P0009R18_mdspan_extents/test.cpp
+++ b/tests/std/tests/P0009R18_mdspan_extents/test.cpp
@@ -545,12 +545,6 @@ static_assert(all_extents_dynamic<dextents<unsigned int, 7>, 7>);
 static_assert(all_extents_dynamic<dextents<unsigned long, 8>, 8>);
 static_assert(all_extents_dynamic<dextents<unsigned long long, 9>, 9>);
 
-// When 'E::rank_dynamic()' is equal to 0 then 'is_empty_v<E>' should be true (MSVC STL specific behavior)
-static_assert(!is_empty_v<dextents<int, 2>>);
-static_assert(!is_empty_v<extents<int, 3, dynamic_extent>>);
-static_assert(is_empty_v<extents<int, 3, 3>>);
-static_assert(is_empty_v<extents<int>>);
-
 int main() {
     static_assert(test());
     test();
diff --git a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp
index b26ca701f86..90d00be88b7 100644
--- a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp
+++ b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp
@@ -486,12 +486,6 @@ constexpr void check_correctness() {
     }
 }
 
-// When 'M::extents_type::rank_dynamic()' is equal to 0 then 'is_empty_v<M>' should be true (MSVC STL specific behavior)
-static_assert(!is_empty_v<layout_left::mapping<dextents<long long, 2>>>);
-static_assert(!is_empty_v<layout_left::mapping<extents<long long, 3, dynamic_extent>>>);
-static_assert(is_empty_v<layout_left::mapping<extents<long long, 3, 3>>>);
-static_assert(is_empty_v<layout_left::mapping<extents<long long>>>);
-
 constexpr bool test() {
     check_members_with_various_extents([]<class E>(const E& e) { check_members(e, make_index_sequence<E::rank()>{}); });
     if (!is_constant_evaluated()) { // too heavy for compile time
diff --git a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp
index 89899ab408c..fa3679ff03f 100644
--- a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp
+++ b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp
@@ -517,12 +517,6 @@ constexpr void check_correctness() {
     }
 }
 
-// When 'M::extents_type::rank_dynamic()' is equal to 0 then 'is_empty_v<M>' should be true (MSVC STL specific behavior)
-static_assert(!is_empty_v<layout_right::mapping<dextents<long, 2>>>);
-static_assert(!is_empty_v<layout_right::mapping<extents<long, 3, dynamic_extent>>>);
-static_assert(is_empty_v<layout_right::mapping<extents<long, 3, 3>>>);
-static_assert(is_empty_v<layout_right::mapping<extents<long>>>);
-
 constexpr bool test() {
     check_members_with_various_extents([]<class E>(const E& e) { check_members(e, make_index_sequence<E::rank()>{}); });
     if (!is_constant_evaluated()) { // too heavy for compile time
diff --git a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp
index f2dadd09de6..dc6daa67e0a 100644
--- a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp
+++ b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp
@@ -821,12 +821,6 @@ constexpr void check_correctness() {
     }
 }
 
-// When 'M::extents_type::rank()' is equal to 0 then 'is_empty_v<M>' should be true (MSVC STL specific behavior)
-static_assert(!is_empty_v<layout_stride::mapping<dextents<long long, 2>>>);
-static_assert(!is_empty_v<layout_stride::mapping<extents<long long, 3, dynamic_extent>>>);
-static_assert(!is_empty_v<layout_stride::mapping<extents<long long, 3, 3>>>);
-static_assert(is_empty_v<layout_stride::mapping<extents<long long>>>);
-
 constexpr bool test() {
     // Check signed integers
     check_members(extents<signed char, 5>{5}, array{1});
diff --git a/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp b/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp
index 330d3cadc34..a36411d53e8 100644
--- a/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp
+++ b/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp
@@ -318,28 +318,6 @@ struct AccessorWithCustomOffsetPolicy {
 
 static_assert(check_accessor_policy_requirements<AccessorWithCustomOffsetPolicy<int>>());
 
-template <class ElementType>
-struct TrivialAccessor {
-    using offset_policy    = TrivialAccessor;
-    using element_type     = ElementType;
-    using reference        = ElementType&;
-    using data_handle_type = ElementType*;
-
-    constexpr reference access(data_handle_type handle, size_t off) const noexcept {
-        return handle[off];
-    }
-
-    constexpr data_handle_type offset(data_handle_type handle, size_t off) const noexcept {
-        return handle + off;
-    }
-
-    int member;
-};
-
-static_assert(check_accessor_policy_requirements<TrivialAccessor<int>>());
-static_assert(is_trivially_copyable_v<TrivialAccessor<int>>);
-static_assert(is_trivially_default_constructible_v<TrivialAccessor<int>>);
-
 template <class Ext, class Layout, template <class> class AccessorTemplate>
 constexpr void check_modeled_concepts_and_member_types() {
     using Accessor = AccessorTemplate<float>;
@@ -1360,25 +1338,6 @@ constexpr void check_deduction_guides() {
     }
 }
 
-// When
-// * 'Mds::accessor_type' is a specialization of 'default_accessor', and
-// * 'Mds::layout_type' is
-//   * 'layout_left' or 'layout_right' and 'Mds::extents_type::rank_dynamic() == 0', or
-//   * 'layout_stride' and 'Mds::extents_type::rank() == 0'
-// then 'sizeof(Mds) == sizeof(void*)' (MSVC STL specific behavior).
-static_assert(sizeof(mdspan<int, extents<int, 3, 3, 3>, layout_left>) == sizeof(void*));
-static_assert(sizeof(mdspan<int, dextents<int, 3>, layout_left>) > sizeof(void*));
-static_assert(sizeof(mdspan<int, extents<int, 3, 3, 3>, layout_left, TrivialAccessor<int>>) > sizeof(void*));
-
-static_assert(sizeof(mdspan<long, extents<long, 2, 2, 2>, layout_right>) == sizeof(void*));
-static_assert(sizeof(mdspan<long, dextents<long, 2>, layout_right>) > sizeof(void*));
-static_assert(sizeof(mdspan<long, extents<long, 2, 2, 2>, layout_right, TrivialAccessor<long>>) > sizeof(void*));
-
-static_assert(sizeof(mdspan<short, extents<short>, layout_stride>) == sizeof(void*));
-static_assert(sizeof(mdspan<short, extents<short, 4, 4, 4>, layout_stride>) > sizeof(void*));
-static_assert(sizeof(mdspan<short, dextents<short, 4>, layout_stride>) > sizeof(void*));
-static_assert(sizeof(mdspan<short, extents<short, 4, 4, 4>, layout_stride, TrivialAccessor<short>>) > sizeof(void*));
-
 constexpr bool test() {
     check_modeled_concepts_and_member_types<dextents<unsigned long long, 3>, layout_left, default_accessor>();
     check_modeled_concepts_and_member_types<dextents<unsigned long long, 3>, layout_right, default_accessor>();
diff --git a/tests/std/tests/P0009R18_mdspan_msabi/env.lst b/tests/std/tests/P0009R18_mdspan_msabi/env.lst
new file mode 100644
index 00000000000..642f530ffad
--- /dev/null
+++ b/tests/std/tests/P0009R18_mdspan_msabi/env.lst
@@ -0,0 +1,4 @@
+# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+RUNALL_INCLUDE ..\usual_latest_matrix.lst
diff --git a/tests/std/tests/P0009R18_mdspan_msabi/test.compile.pass.cpp b/tests/std/tests/P0009R18_mdspan_msabi/test.compile.pass.cpp
new file mode 100644
index 00000000000..0d85c79d2be
--- /dev/null
+++ b/tests/std/tests/P0009R18_mdspan_msabi/test.compile.pass.cpp
@@ -0,0 +1,54 @@
+// Copyright (c) Microsoft Corporation.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+// Tests MSVC STL specific behavior on ABI.
+
+#include <mdspan>
+#include <type_traits>
+
+#include <test_mdspan_support.hpp>
+
+using namespace std;
+
+// When 'E::rank_dynamic()' is equal to 0 then 'is_empty_v<E>' should be true
+static_assert(!is_empty_v<dextents<int, 2>>);
+static_assert(!is_empty_v<extents<int, 3, dynamic_extent>>);
+static_assert(is_empty_v<extents<int, 3, 3>>);
+static_assert(is_empty_v<extents<int>>);
+
+// When 'M::extents_type::rank_dynamic()' is equal to 0 then 'is_empty_v<M>' should be true
+static_assert(!is_empty_v<layout_left::mapping<dextents<long long, 2>>>);
+static_assert(!is_empty_v<layout_left::mapping<extents<long long, 3, dynamic_extent>>>);
+static_assert(is_empty_v<layout_left::mapping<extents<long long, 3, 3>>>);
+static_assert(is_empty_v<layout_left::mapping<extents<long long>>>);
+
+// When 'M::extents_type::rank_dynamic()' is equal to 0 then 'is_empty_v<M>' should be true
+static_assert(!is_empty_v<layout_right::mapping<dextents<long, 2>>>);
+static_assert(!is_empty_v<layout_right::mapping<extents<long, 3, dynamic_extent>>>);
+static_assert(is_empty_v<layout_right::mapping<extents<long, 3, 3>>>);
+static_assert(is_empty_v<layout_right::mapping<extents<long>>>);
+
+// When 'M::extents_type::rank()' is equal to 0 then 'is_empty_v<M>' should be true
+static_assert(!is_empty_v<layout_stride::mapping<dextents<long long, 2>>>);
+static_assert(!is_empty_v<layout_stride::mapping<extents<long long, 3, dynamic_extent>>>);
+static_assert(!is_empty_v<layout_stride::mapping<extents<long long, 3, 3>>>);
+static_assert(is_empty_v<layout_stride::mapping<extents<long long>>>);
+
+// When
+// * 'Mds::accessor_type' is a specialization of 'default_accessor', and
+// * 'Mds::layout_type' is
+//   * 'layout_left' or 'layout_right' and 'Mds::extents_type::rank_dynamic() == 0', or
+//   * 'layout_stride' and 'Mds::extents_type::rank() == 0'
+// then 'sizeof(Mds) == sizeof(void*)'.
+static_assert(sizeof(mdspan<int, extents<int, 3, 3, 3>, layout_left>) == sizeof(void*));
+static_assert(sizeof(mdspan<int, dextents<int, 3>, layout_left>) > sizeof(void*));
+static_assert(sizeof(mdspan<int, extents<int, 3, 3, 3>, layout_left, TrivialAccessor<int>>) > sizeof(void*));
+
+static_assert(sizeof(mdspan<long, extents<long, 2, 2, 2>, layout_right>) == sizeof(void*));
+static_assert(sizeof(mdspan<long, dextents<long, 2>, layout_right>) > sizeof(void*));
+static_assert(sizeof(mdspan<long, extents<long, 2, 2, 2>, layout_right, TrivialAccessor<long>>) > sizeof(void*));
+
+static_assert(sizeof(mdspan<short, extents<short>, layout_stride>) == sizeof(void*));
+static_assert(sizeof(mdspan<short, extents<short, 4, 4, 4>, layout_stride>) > sizeof(void*));
+static_assert(sizeof(mdspan<short, dextents<short, 4>, layout_stride>) > sizeof(void*));
+static_assert(sizeof(mdspan<short, extents<short, 4, 4, 4>, layout_stride, TrivialAccessor<short>>) > sizeof(void*));


Reviews:
user: login: StephanTLavavej
state: COMMENTED
author_association: MEMBER
submitted_at: 2024-10-20T16:31:00Z
user: login: StephanTLavavej
state: APPROVED
author_association: MEMBER
submitted_at: 2024-10-20T16:32:26Z
user: login: StephanTLavavej
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-03T20:27:40Z
user: login: StephanTLavavej
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-03T20:29:27Z

Review comments:
diff_hunk: @@ -0,0 +1,77 @@
+// Copyright (c) Microsoft Corporation.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+// Tests MSVC STL specific behavior on ABI.
+
+#include <cstddef>
+#include <mdspan>
+#include <type_traits>
+
+#include <test_mdspan_support.hpp>
+
+using namespace std;
+
+template <class ElementType>
+struct TrivialAccessor {
path: tests/std/tests/P0009R18_mdspan_msabi/test.compile.pass.cpp
user: login: StephanTLavavej
body: Since we already have `test_mdspan_support.hpp`, let's lift up `TrivialAccessor` instead of duplicating it.
created_at: 2024-10-20T15:50:12Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,55 @@
+// Copyright (c) Microsoft Corporation.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+// Tests MSVC STL specific behavior on ABI.
+
+#include <cstddef>
+#include <mdspan>
+#include <type_traits>
+
+#include <test_mdspan_support.hpp>
+
+using namespace std;
+
+// When 'E::rank_dynamic()' is equal to 0 then 'is_empty_v<E>' should be true
+static_assert(!is_empty_v<dextents<int, 2>>);
+static_assert(!is_empty_v<extents<int, 3, dynamic_extent>>);
+static_assert(is_empty_v<extents<int, 3, 3>>);
+static_assert(is_empty_v<extents<int>>);
+
+// When 'M::extents_type::rank_dynamic()' is equal to 0 then 'is_empty_v<M>' should be true
+static_assert(!is_empty_v<layout_left::mapping<dextents<long long, 2>>>);
+static_assert(!is_empty_v<layout_left::mapping<extents<long long, 3, dynamic_extent>>>);
+static_assert(is_empty_v<layout_left::mapping<extents<long long, 3, 3>>>);
+static_assert(is_empty_v<layout_left::mapping<extents<long long>>>);
+
+// When 'M::extents_type::rank_dynamic()' is equal to 0 then 'is_empty_v<M>' should be true
+static_assert(!is_empty_v<layout_right::mapping<dextents<long, 2>>>);
+static_assert(!is_empty_v<layout_right::mapping<extents<long, 3, dynamic_extent>>>);
+static_assert(is_empty_v<layout_right::mapping<extents<long, 3, 3>>>);
+static_assert(is_empty_v<layout_right::mapping<extents<long>>>);
+
+// When 'M::extents_type::rank()' is equal to 0 then 'is_empty_v<M>' should be true
+static_assert(!is_empty_v<layout_stride::mapping<dextents<long long, 2>>>);
+static_assert(!is_empty_v<layout_stride::mapping<extents<long long, 3, dynamic_extent>>>);
+static_assert(!is_empty_v<layout_stride::mapping<extents<long long, 3, 3>>>);
+static_assert(is_empty_v<layout_stride::mapping<extents<long long>>>);
+
+// When
+// * 'Mds::accessor_type' is a specialization of 'default_accessor', and
+// * 'Mds::layout_type' is
+//   * 'layout_left' or 'layout_right' and 'Mds::extents_type::rank_dynamic() == 0', or
+//   * 'layout_stride' and 'Mds::extents_type::rank() == 0'
+// then 'sizeof(Mds) == sizeof(void*)' (MSVC STL specific behavior).
path: tests/std/tests/P0009R18_mdspan_msabi/test.compile.pass.cpp
user: login: StephanTLavavej
body: Nitpick: All of the other moved comments were edited to avoid duplicating the comment on line 4 of this file.
created_at: 2025-03-03T20:12:23Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,55 @@
+// Copyright (c) Microsoft Corporation.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+// Tests MSVC STL specific behavior on ABI.
+
+#include <cstddef>
path: tests/std/tests/P0009R18_mdspan_msabi/test.compile.pass.cpp
user: login: StephanTLavavej
body: Nitpick: `<cstddef>` is unused (this test doesn't actually say `size_t` anywhere).
created_at: 2025-03-03T20:14:45Z
author_association: MEMBER

PR comments:
user: login: frederick-vs-ja
created_at: 2024-09-17T16:34:20Z
body: Oops, can we continue on this as NVCC doesn't seem to support either `[[no_unique_address]]` or `[[msvc::no_unique_address]]`?
author_association: CONTRIBUTOR
user: login: CaseyCarter
created_at: 2024-09-17T18:47:56Z
body: I'm going to mark this "decision needed" so we can discuss it in the maintainer meeting this week. We need to decide how we're going to communicate to Nvidia and Intel that this is going to happen and that they'll need to implement this part of the ABI to travel into the future with us.
author_association: CONTRIBUTOR
user: login: StephanTLavavej
created_at: 2024-09-18T21:23:40Z
body: I've communicated this upcoming change to our contacts at NVIDIA. As CUDA 12.4.0 doesn't support C++23, you can revert your experimental test change here.

We don't officially support Intel's compiler, but as it's Clang-based we expect that it won't have problems understanding this attribute.
author_association: MEMBER
user: login: StephanTLavavej
created_at: 2024-09-28T20:35:46Z
body: I pushed a merge with `main` to resolve a trivial adjacent-add conflict in `<xmemory>`.
author_association: MEMBER
user: login: CaseyCarter
created_at: 2024-10-16T05:37:14Z
body: @frederick-vs-ja Good news: DevCom-10588147 is fixed in 17.12p3. Bad news: Now only EDG fails the repro. =/

Did you file a separate bug against EDG, or should we do so now? I don't think it should block us doing C++23 ABI work since EDG isn't a codegen compiler for C++23 yet. We know NVCC is working on C++23 support, but if we poke EDG they may be ready when NVCC gets there.
author_association: CONTRIBUTOR
user: login: frederick-vs-ja
created_at: 2024-10-17T23:49:39Z
body: > @frederick-vs-ja Good news: DevCom-10588147 is fixed in 17.12p3. Bad news: Now only EDG fails the repro. =/

I mentioned this in DevCom-10747012. EDG recognizes `[[no_unique_address]]` but not `[[msvc::no_unique_address]]` in VS IntelliSense, which is quite unfortunate. And it seems that EDG became more buggy on this recently...

This shouldn't be an issue. IntelliSense defines `__INTELLISENSE__`, but `cl /BE` (used in STL testing) does not, so we can detect the difference if we must. We allowed IntelliSense to see concepts in the headers for a while this way before EDG concepts support was complete enough to enable test coverage.

author_association: CONTRIBUTOR
user: login: StephanTLavavej
created_at: 2024-10-20T16:32:53Z
body: Thanks, this is awesome! :heart_eyes_cat: I pushed a conflict-free merge with `main` and a trivial test change to avoid duplication.
author_association: MEMBER
user: login: StephanTLavavej
created_at: 2024-10-20T22:35:28Z
body: There are test failures, all in `llvm-project\libcxx\test\std\containers\views\mdspan\mdspan`. Since we recently updated the toolset, I suspect an MSVC compiler regression, but I haven't tracked down the root cause yet.
author_association: MEMBER
user: login: StephanTLavavej
created_at: 2024-10-21T17:11:38Z
body: This is blocked by DevCom-10754777 VSO-2266748 "Wrong code generation when initializing a variable of class type that contains multiple instances of the same empty class in its layout", fixed by MSVC-PR-583942 on 2024-10-10, shipping in 17.13p2.

<details><summary>Click to expand fully reduced repro:</summary>

```
C:\Temp>type meow.cpp
```
```cpp
#include <cassert>

using Extents = signed char;

struct Holder {
    constexpr Holder(const Extents& e) : exts(e) {}
    [[msvc::no_unique_address]] Extents exts;
};

struct Empty {};

struct Mdspan {
    [[msvc::no_unique_address]] Holder holder;
    [[msvc::no_unique_address]] Empty acc{};
    [[msvc::no_unique_address]] int val{1729};
};

int main() {
    Extents extents{7};
    Mdspan md{extents};
    assert(md.holder.exts == extents);
}
```

17.12 Preview 3 x64:

```
C:\Temp>cl /EHsc /nologo /W4 /std:c++latest /MTd /Od /d1reportSingleClassLayoutMdspan meow.cpp && meow
meow.cpp

class Mdspan    size(4):
        +---
 0      | Holder holder
 0      | Empty acc
 0      | val
        +---
Assertion failed: md.holder.exts == extents, file meow.cpp, line 21
```

Current `prod/fe` x64:

```
C:\Temp>cl /EHsc /nologo /W4 /std:c++latest /MTd /Od /d1reportSingleClassLayoutMdspan meow.cpp && meow
meow.cpp

class Mdspan    size(8):
        +---
 0      | Holder holder
 0      | Empty acc
        | <alignment member> (size=3)
 4      | val
        +---

C:\Temp>
```
</details>

We talked about this in Discord, and I now agree with @AlexGuteniev that all use of `[[msvc::no_unique_address]]` should be avoided until we have this fix - even though only `mdspan` is directly affected now, user usage could reveal other problems, and since it happens only at runtime in a catastrophic way, this isn't worth the risk when the compiler is known to be unreliable.

* [x] Merge with the VS 2022 17.13 Preview 2 toolset update.
* [x] Verify that `llvm-project\libcxx\test\std\containers\views\mdspan\mdspan` passes again.
author_association: MEMBER
user: login: StephanTLavavej
created_at: 2024-10-26T14:59:17Z
body: Resolved a trivial merge conflict in `<xmemory>` with #4987. (Set to draft mode to avoid running the CI while this PR is blocked.)
author_association: MEMBER
user: login: StephanTLavavej
created_at: 2024-12-11T04:57:54Z
body: 17.13p2 is now publicly available, but I just realized that we'll also need to wait for the MSVC-internal toolset to be updated.

As of 2025-01-07, the internal toolset is still 17.12-based.
author_association: MEMBER
user: login: StephanTLavavej
created_at: 2025-01-29T22:14:52Z
body: As of 2025-01-29, @MahmoudGSaleh reports that the internal toolset update is being worked on this week, so we should have happy news to report soon. :cat:

Edit: Said toolset update MSVC-PR-606512 is actually picking up 17.12.2 for some reason; I've asked why and whether 17.13 Preview 2/4 can be picked up.
author_association: MEMBER
user: login: StephanTLavavej
created_at: 2025-01-31T10:02:43Z
body: VSO-2355645 DevCom-10837331 "`std::set` broken under VS 17.12.4 with `no_unique_address` comparator" may affect this PR. A fix was merged for 17.14 Preview 3.
author_association: MEMBER
user: login: StephanTLavavej
created_at: 2025-02-05T22:44:40Z
body: MSVC-PR-608181 is updating the toolset to 17.14 Preview 1 :tada: :heart_eyes_cat: and is expected to be merged soon (to the MSVC `main` branch, after which it will flow into our usual home `prod/fe`).
author_association: MEMBER
user: login: StephanTLavavej
created_at: 2025-02-21T16:32:36Z
body: Internal MSVC-PR-608181 has finally updated the toolset to 17.14 Preview 1 in MSVC `main`, picking up the fix for DevCom-10754777 "Wrong code generation when initializing a variable of class type that contains multiple instances of the same empty class in its layout".

Now we're just waiting for MSVC `main` to be merged into MSVC `prod/fe` which happens fairly frequently.
author_association: MEMBER
user: login: StephanTLavavej
created_at: 2025-02-26T20:00:50Z
body: We've finally gotten an internal toolset update so this PR is unblocked.
author_association: MEMBER
user: login: StephanTLavavej
created_at: 2025-03-21T14:18:25Z
body: I'm mirroring this to the MSVC-internal repo - please notify me if any further changes are pushed.
author_association: MEMBER
user: login: StephanTLavavej
created_at: 2025-03-25T03:23:40Z
body: Thanks for implementing these long-awaited space optimizations! :artificial_satellite: :rocket: :ringed_planet:
author_association: MEMBER