<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/109996>109996</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            [libc++] `std::equal` possibly redundant unwrap of iterators
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            libc++
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          akukh
      </td>
    </tr>
</table>

<pre>
    Attempting to call `std::equal` on two ranges of elements for comparison fails in constant expression. See the following example (sorry in advance for a lot of code):
<details>
  <summary>Code</summary>

```c++
#include <utility>
#include <algorithm>
#include <array>
#include <vector>

struct nontrivial_type {
    constexpr nontrivial_type() noexcept {}
};

template <typename T>
union [[nodiscard]] uninitialized_storage final {
    using value_type = T;

 value_type      value;
    nontrivial_type _;

    constexpr uninitialized_storage() : _{} {}

    constexpr uninitialized_storage(uninitialized_storage const& other) noexcept
 : value{other.value} {}
    constexpr uninitialized_storage(uninitialized_storage&& other) noexcept
 : value{std::move(other.value)} {}

    constexpr uninitialized_storage& operator=(uninitialized_storage const&) noexcept = delete;
    constexpr uninitialized_storage& operator=(uninitialized_storage&&) noexcept = delete;

    constexpr ~uninitialized_storage() {}
};

struct nontrivial_int {
    int x;

    constexpr nontrivial_int() noexcept : x{0} {}

 constexpr nontrivial_int(int val) noexcept : x{val} {}
    constexpr ~nontrivial_int() {}

    constexpr nontrivial_int(nontrivial_int const& other) noexcept {
        x = other.x;
    }
    constexpr nontrivial_int(nontrivial_int&& other) noexcept {
        x = other.x;
 }

    constexpr nontrivial_int& operator=(nontrivial_int const& other) noexcept {
        x = other.x;
        return *this;
 }
    constexpr nontrivial_int& operator=(nontrivial_int&& other) noexcept {
        x = other.x;
        return *this;
    }

 constexpr bool operator==(nontrivial_int const& other) const noexcept {
 return x == other.x;
    }
};

template <typename It, typename Fn>
class iterator_adapter final {
    using functor_type  = Fn;

public:
    using reference         = decltype(::std::declval<Fn>()(*::std::declval<It>()));
    using value_type        = ::std::remove_reference_t<reference>;
    using pointer           = ::std::add_pointer_t<value_type>;
    using difference_type   = ::std::iter_difference_t<It>;
    using iterator_type = It;
    using iterator_category = ::std::random_access_iterator_tag;
    using iterator_concept  = ::std::contiguous_iterator_tag;

    iterator_type it_;
 functor_type  functor_;

    constexpr iterator_adapter() noexcept : it_{}, functor_{} {}

    constexpr iterator_adapter(iterator_type const& it, functor_type adapter = {}) noexcept
            : it_{it}, functor_{adapter} {}

    [[nodiscard]] constexpr reference operator*() const noexcept {
        return *operator->();
    }

 [[nodiscard]] constexpr pointer operator->() const noexcept {
 return ::std::addressof(functor_(*it_));
    }

    [[nodiscard]] constexpr reference operator[](difference_type index) const noexcept {
 return functor_(it_[index]);
    }

    constexpr iterator_adapter& operator++() noexcept {
        ++it_;
        return *this;
    }

    constexpr iterator_adapter operator++(int) noexcept {
        return {it_++, functor_};
    }

    constexpr iterator_adapter& operator--() noexcept {
        --it_;
        return *this;
    }

    constexpr iterator_adapter operator--(int) noexcept {
        return {it_--, functor_};
    }

    constexpr iterator_adapter& operator+=(difference_type n) noexcept {
        it_ += n;
        return *this;
    }

    constexpr iterator_adapter& operator-=(difference_type n) noexcept {
        it_ -= n;
 return *this;
    }

    constexpr iterator_adapter operator+(difference_type n) const noexcept {
        return {it_ + n, functor_};
    }

    constexpr iterator_adapter operator-(difference_type n) const noexcept {
        return {it_ - n, functor_};
    }

    [[nodiscard]] constexpr difference_type
 operator-(iterator_adapter const& other) const noexcept {
        return it_ - other.it_;
    }

    [[nodiscard]] constexpr std::strong_ordering
    operator<=>(iterator_adapter const& other) const noexcept {
        return it_ <=> other.it_;
    }

 [[nodiscard]] constexpr bool operator==(iterator_adapter const& other) const noexcept {
        return it_ == other.it_;
    }

    friend constexpr auto operator+(difference_type n, iterator_adapter const& a) noexcept {
        return iterator_adapter{a.it_ + n, a.functor_};
 }
};

template <typename T, ::std::size_t N>
struct static_vector final {
    struct [[nodiscard]] mapper final {
        static constexpr T& operator()(uninitialized_storage<T>& storage) noexcept {
 return storage.value;
        }
        static constexpr T const& operator()(uninitialized_storage<T> const& storage) noexcept {
 return storage.value;
        }
    };

    using iterator = iterator_adapter<
        typename ::std::array<uninitialized_storage<T>, N>::iterator,
        mapper
 >;
    using const_iterator = iterator_adapter<
        typename ::std::array<uninitialized_storage<T>, N>::const_iterator,
 mapper
    >;

    ::std::array<uninitialized_storage<T>, N> storage_;
    ::std::size_t                             size_;

 constexpr static_vector() noexcept : size_{} {
        if consteval {
 ::std::construct_at(::std::addressof(storage_));
        }
 }

    constexpr iterator begin() noexcept {
        return {storage_.begin()};
    }
    constexpr const_iterator begin() const noexcept {
        return {storage_.begin()};
    }
    constexpr iterator end() noexcept {
        return {storage_.begin() + size_};
 }
    constexpr const_iterator end() const noexcept {
        return {storage_.begin() + size_};
    }

    template <typename... Args>
    constexpr T& emplace_back(Args&&... args) noexcept {
 ::std::construct_at(end().operator->(), ::std::forward<Args>(args)...);
 ++size_;
        return storage_[size_ - 1].value;
    }

 template <::std::size_t OtherCapacity>
    constexpr bool operator==(static_vector<T, OtherCapacity> const& other) const noexcept {
 return ::std::equal(begin(), end(), other.begin());
 }
};

int main() {
    constexpr auto result = [] {
 static_vector<nontrivial_int, 3> v1;
        v1.emplace_back(1);
 v1.emplace_back(2);

        static_vector<nontrivial_int, 3> v2;
 v2.emplace_back(1);
        v2.emplace_back(2);

 return v1 == v2;
    }();
    static_assert(true == result);
}
```

</details>

Fails with the following errors:
<details>
 <summary>Output</summary>

```
<source>:203:20: error: constexpr variable 'result' must be initialized by a constant expression
  203 | constexpr auto result = [] {
      |                    ^        ~~~~
 204 |         static_vector<nontrivial_int, 3> v1;
      | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  205 | v1.emplace_back(1);
      |         ~~~~~~~~~~~~~~~~~~~
  206 | v1.emplace_back(2);
      |         ~~~~~~~~~~~~~~~~~~~
  207 | 
 208 |         static_vector<nontrivial_int, 3> v2;
      | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  209 | v2.emplace_back(1);
      |         ~~~~~~~~~~~~~~~~~~~
  210 | v2.emplace_back(2);
      |         ~~~~~~~~~~~~~~~~~~~
  211 | 
 212 |         return v1 == v2;
      |         ~~~~~~~~~~~~~~~~
  213 | }();
      | ~~~
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__algorithm/comp.h:25:16: note: member call on dereferenced one-past-the-end pointer is not allowed in a constant expression
   25 |     return __x == __y;
      | ^
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__algorithm/equal.h:42:10: note: in call to '__pred.operator()<nontrivial_int, nontrivial_int>(*(&v1.storage_.__elems_[0].value + 1), *(&v2.storage_.__elems_[0].value + 1))'
   42 |     if (!__pred(*__first1, *__first2))
      | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__algorithm/equal.h:61:10: note: in call to '__equal_iter_impl<const nontrivial_int *, const nontrivial_int *, std::__equal_to>(&v1.storage_.__elems_[0].value + 1, &v1.storage_.__elems_[2].value, &v2.storage_.__elems_[0].value + 1, __pred)'
   61 |   return std::__equal_iter_impl(
      | ^~~~~~~~~~~~~~~~~~~~~~~
   62 |       std::__unwrap_iter(__first1), std::__unwrap_iter(__last1), std::__unwrap_iter(__first2), __pred);
 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__algorithm/equal.h:68:10: note: in call to 'equal<iterator_adapter<const uninitialized_storage<nontrivial_int> *, static_vector<nontrivial_int, 3>::mapper>, iterator_adapter<const uninitialized_storage<nontrivial_int> *, static_vector<nontrivial_int, 3>::mapper>, std::__equal_to>({&v1.storage_.__elems_[0], {}}, {&v1.storage_.__elems_[2], {}}, {&v2.storage_.__elems_[0], {}}, {})'
   68 |   return std::equal(__first1, __last1, __first2, __equal_to());
      | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:198:16: note: in call to 'equal<iterator_adapter<const uninitialized_storage<nontrivial_int> *, static_vector<nontrivial_int, 3>::mapper>, iterator_adapter<const uninitialized_storage<nontrivial_int> *, static_vector<nontrivial_int, 3>::mapper>>({&v1.storage_.__elems_[0], {}}, {&v1.storage_.__elems_[2], {}}, {&v2.storage_.__elems_[0], {}})'
  198 | return ::std::equal(begin(), end(), other.begin());
      | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:212:16: note: in call to 'v1.operator==<3UL>(v2)'
  212 |         return v1 == v2;
 |                ^~~~~~~~
<source>:203:29: note: in call to '[] {
 static_vector<nontrivial_int, 3> v1;
    v1.emplace_back(1);
 v1.emplace_back(2);
    static_vector<nontrivial_int, 3> v2;
 v2.emplace_back(1);
    v2.emplace_back(2);
    return v1 == v2;
}.operator()()'
  203 |     constexpr auto result = [] {
      | ^~~~
  204 | static_vector<nontrivial_int, 3> v1;
      | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  205 | v1.emplace_back(1);
      |         ~~~~~~~~~~~~~~~~~~~
  206 | v1.emplace_back(2);
      |         ~~~~~~~~~~~~~~~~~~~
  207 | 
 208 |         static_vector<nontrivial_int, 3> v2;
      | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  209 | v2.emplace_back(1);
      |         ~~~~~~~~~~~~~~~~~~~
  210 | v2.emplace_back(2);
      |         ~~~~~~~~~~~~~~~~~~~
  211 | 
 212 |         return v1 == v2;
      |         ~~~~~~~~~~~~~~~~
  213 | }();
      |     ~~~
<source>:214:19: error: static assertion expression is not an integral constant expression
  214 | static_assert(true == result);
      | ^~~~~~~~~~~~~~
<source>:214:27: note: initializer of 'result' is not a constant expression
  214 |     static_assert(true == result);
      | ^
<source>:203:20: note: declared here
  203 | constexpr auto result = [] {
      |                    ^
2 errors generated.
```

</details>

Compiler options:
```
-std=c++23 -stdlib=libc++
```
[godbolt](https://godbolt.org/z/xETjjdMhf)

GCC with their `libstdc++` compiles and runs successfully. In this question, is the problem with `libc++` or does `libstdc++` incorrectly implement `std::equal`?
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzsW0tv4zjy_zTMhbBhUfHrkEMix38M8N-dw86eBVqibc7IpIak3Mkc8tkXJPWiRMnKo3tngTYa7khiVf2qWEVWlWgsJT0xQh7A8gksd3e4UGcuHvAfxR_nuwNPXx8elSKXXFF2gorDBGcZBKuFVCkIH0H4SP4scAZWC8gZVN84FJidiIT8CElGLoQpCY9cwIRfciyo5AweMc0kpAwmnEmFmYLkJRdESsrZHP6LEKjOBB55lvFvWip5wZc8IxCgjeRCvGpSnF4xS4hhjWHGlRaY8JQAtNW4FjuweARhlBKlpYHw2d6CEISRLC4XLF5B-BxpijACaN_cK2nt92ph_yUAPel_9i4KKUuyIiWaW6FoRlWL0nmKsxMXVJ0vQ8-FwEO0V5IoLjqQpBJFoiDjTAl6pTiL1WtOIFg_VRpCa1ht1O4wgDYAbSHj5CUhuTJU613Jfr0D4VNblJ73DCuDRVMzfCHwtxpPwShn0PjNE-MplQkWKVjuwHIHC0YZVRRn9C-SxlJxgU8EHinDmQu1kHqKrzgrSKlIuNMyHCDt5-ZjrutB-k7XHnGXhWMWL7zSOCB8hLE1jGufd_Dxq2_oAFpBrs5EtCei5K1FW9XWT2bMvLxykXwCA0CrqQDqCL_wq-bXBoS2n7DOCvKcCGxce3fTWK6_hjuYkowod_K_RqS1zS15Pqlvo_40GmP9cKZMuSGib7yMynepe0EePsIXsH5aDMzZCB8t-oozLzt9f9Qx37ywbjlNj6hjmuEgcq2mPy9mAq3jvjgeMwD5hvCh6Jks-V2ad532uxhCfwRRhWAQoEd1ptKD9jM4P22zGyDbk9lz6APnmYNumiHNHR_SEoRBetO1Jm6ovyiAIlhf7lm9wSYZlhJSZeHHOMW5ImJ4Fz0WTCcM5T6p8WlmDoK8OGQ0qTOkhlaQIxFEp1TVx659SVblDWYvqDcF_USvAGFkAZvgNt-PgyN_Ua2R5l84lgm0cHRYCqL3pLjGHCsQRvWVFtJnnHPKtPUgHGGM0zQuBxqeDRo_05QeawgWsoepnsC4PbI2RZ9hPdl1LqRHDg9LsCInLl69VsIs5ZcYJwmRMm4449MoR86M0_s4Jpwpeip4McCutWc5elDVpGMdL62uRve3bgj4djgtw24tKGq4TkvjPPxdBeolgqo2e_OsCktjrxJBL7Fy3K4CS1UfboVgELU_3250aQK5XvfQY2mwwXWtt8ZWtLMmYsdW3FugquDrs7251vYjVJeK_AjQpjabWXa0TfurytdY0NTGAG268U5ZSl6maNGCqnEunyylZnoT75iftvZeW6B6Cjxniu0wJyDfv8GOguojMlnAGKhK9NoAK8naYbH7GhvNZjftM5v9ENsYJO-ziyb5DjbRtg59vs3GsVEVQ0sL2XezlzN5H4Y564D8Pp7uRzZ1yTVTrA2qyb5omlum-xJ0s3dju7XUdkCVhG3YPZ3eka27SlgNbMbejfEPQK93JKkEZ6eYi5QIyk4Nh6buiEy58Pzl6tScJ6p1Syd_ufTVmFuV04R5OApKWNoCiQvFb0de1I-GGjaetOr21qL1E547QYrn3lB4f0cVRd00R9K_SKzgP-tasOwTSYUVTWLbGfZVguU4_0xfcJ77C0hLqnm3LP2bu0-UFZ6_1xVGpjGMVrDufnltXBq3HDTvN3Jhtzvjh9ZywvcAbMi-GmZ_smGvuDI1Qs-vwshlWztGN_W1rwqi8QmIrNPUlac1TUeEdYTKX72VqDFU_N9C7kpv8DvATTH17LH6J8RXU95Zl7zROfYxQ7rQ2rtHK4595aylb8pXN6k5lqyuThz3q3W7GsRY9Rs47UKq1rlfP7l-Pin9gAdyouxmpt3kFpX4eYtwMLlwZXa8tC15em7zCfm1ZMLST2ps9pVy2n27yajijfjPqT0Awr81-3az-XwOH8Wp_cYVdrcUQ5aQ-ICTPwDamOGmS6yJsbnymnHcv2sTzD3Ni_4Oe-Tim94Zw6iCizal7Pl87oSBrUjdiO6Ys46gpR0HZzAAy51n4-jasW1E_zLzq06VIpzjpP2a2bWrP3NzVxm90KGox-4D_e8OTnsEAG3a4YOixif1hc33nBHbSSkTZQpecOOf_vfcJikURBaZfWlnOzat0V1TdF9SRDDUtrgGvRm-BvOOwwYO9v5z1H7uS2Ruw0At_mhcfgWzN8wHo5zAa1Dl4W1JpXf2230laiwlETrUlChIxcAa3RFVT2Z1eqINwR636J7LsN97czLkG1Xn7hEQIbiQowc7nHMdvxYqL9Skkx01R8kLUb48eESL0HzrndjI1n803nbFguKDOZKyrvRfw0shFTwQ2Eoy4OEVYt85l8q2aBFCsI7e48nllhz5sg6wfK7-fHt7eysp0OLeIfhoLGgebxM-jXJLQ3MrgnoajfJcDfBEn-C5NkNrc20-ZC70aXNtrWpTAn6qasFigOcnzBUEjrkC5FDeWmLG5TRCbGD4F6TauGX0oj3PFUD7hF9ymhExIy95xoXezfZJhtlpFmzmwXwB0P6gt5K93uH35VErPaZqOu-vAUD7OG6ObVmm87NeD5YgfAxWei1gXBH9_4VcDkTYU3GcwZTULw9SyBmZ5ViqmTqTGWFp_RaESk0PsV7dSGqOs42tERAta5OVxo3j-hV0HL96TAOWzz_CNGbnN7a5R9o2i7ZtKLN2UVyvk3GcC5LOO9W6L5o6d8oczr7DWl2DeZ23xjHJyEXqpGtRJ1smgw2qnK8mQ9PJtgCta3veN85Nj9AwC6wqFlUcH6mQKiillZeoZNSflZvrwI-asFVwc8LMYFNjxPSSZyCMqsTQPaykjRzBsWd1qlgxVbya1-kzqg08NBo1yXY5buqER7CaTmfeV0E573WC39GgMQvaTJ7nhn17zWzxLtg3gXPDHKBN41zbjhW74zI8aVjLOdt6t_LwaVvWZz4_2M03N9zcFi9h5GluWY8e6hr1lqnG2afkC-XZStvPsv2nvwGE4UBdP43Hqok7e2JhXV-MxusQxXDkeinMeQsndDcDoVvVqe01uw4d_WcVHvrvWv1-tTpxPb8VAW7JEWw3vezip6cOQPjbumTbEYOtdcTv1TT5mBv6690A3XC-azDvtJfCKPz3_9uZuCJX83eVBJ5StlForDrfDqP9ovbPJ1s_HyweJ9R_E8q5MZuD9a6biJff625jAr6vzdbxyYab7UD87Dx0ef7sPPwvdh5K-oG19N5s507vsHyBbTuolLNWmV-3AxikTJGTwNloxzBwQmliT3Z4sxjRAa3dFbba2AXkR7f1WakwBTh8d0PZ19YY7tdWeFOSZFiQFJ6JIN-v32pHobJFDU-E6XWVpPMP9sCjsh6CPNeO0mp6u5xmJpPYlWUQCqG-kdEDCHcZPbi_V-xgWD6deHrgmbKHWs9K5UYM2gO0Lx_NuTgBtP8LoP3L82-__57-43ysOxr2-_-iqO7VUwHBapHRg1RpJXu1gGVxJyFmKRQFk1AW5jj6sciy1zn8hUF1phL-WRCptTW5pTTN_1zwQ0YuVoLl3WLMBUw5kV6hlCVcCJKo7BXqAt38CtX7m1UQ7u_ShzDdhlt8Rx6CNVqj9SpYre7OD8lqEZDtcbvCqzUJj-khXIWbLTnebzb3YZre39EHtED3iy1aBvfL1WI13ySrY3pEGOF1uEHHENwvyAXTbJ5l14u25x2VsiAPwWK73a7uMnwgmTS_u0WopR3SieideNBUs0NxkuB-kVGpZMNHUZWZX-y2yLTP-n6Wm3Mp6SF7hYKkBUt1bNqugA7iKo-Xd4XIHjqOQNW5OMwTrgtqLbr8b5YL_jtJdO1u9JEA7UuVrg_oPwEAAP__KQUe6w">