<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/61718>61718</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
std::uninitialized_copy_n does not seem to handle exceptions correctly
</td>
</tr>
<tr>
<th>Labels</th>
<td>
new issue
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
Akashprem
</td>
</tr>
</table>
<pre>
This is a sample example to trigger this issue:
```
#include <cassert>
#include <memory>
#include <string>
template <typename T>
struct Iterator final
{
using value_type = T;
using difference_type = std::ptrdiff_t;
using reference = value_type const &;
using pointer = value_type const *;
using iterator_category = std::input_iterator_tag;
Iterator(
std::size_t index,
std::size_t exception_index,
T const &obj)
: index{index}, exception_index{exception_index}, obj{obj}
{
}
Iterator &operator++()
{
this->index += 1;
if (this->index >= this->exception_index)
{
throw std::exception{};
}
return *this;
}
Iterator operator++(int)
{
Iterator temp = *this;
++(*this);
return temp;
}
reference operator*()
{
return this->obj;
}
std::size_t index;
std::size_t exception_index;
T obj;
};
int main()
{
using InIt = Iterator<std::shared_ptr<std::string>>;
auto obj = std::make_shared<std::string>("Hello World");
{
std::size_t start_index = 0;
std::size_t exception_index = 1;
std::size_t end_index = 2;
InIt const start{start_index, exception_index, obj};
std::shared_ptr<std::string> *p;
std::size_t sz;
std::tie(p, sz) = std::get_temporary_buffer<std::shared_ptr<std::string>>(end_index - start_index);
assert(sz == end_index - start_index);
try
{
std::uninitialized_copy_n(start, sz, p);
}
catch (...)
{
}
}
assert(obj.use_count() == 1);
}
```
The `assert(obj.use_count() == 1);` at the end, fails:
```
Assertion failed: (obj.use_count() == 1), function main, file test.cpp, line 70.
zsh: abort ./a.out
```
because `obj.use_count()` is actually `2`.
The compiler flags I used:
```
clang++ -nostdinc++ -nostdlib++ -isystem llvm-project-install/usr/local/include/c++/v1 -L llvm-project-install/usr/local/lib -Wl,-rpath,llvm-project-install/usr/local/lib -lc++ -g3 test.cpp
```
I was able to track the reason for the issue to here:
```
template <class _ValueType, class _InputIterator, class _Size, class _ForwardIterator, class _Sentinel>
inline _LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator>
__uninitialized_copy_n(_InputIterator __ifirst, _Size __n,
_ForwardIterator __ofirst, _Sentinel __olast) {
_ForwardIterator __idx = __ofirst;
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
try {
#endif
for (; __n > 0 && __idx != __olast; ++__ifirst, (void)++__idx, (void)--__n)
::new (_VSTD::__voidify(*__idx)) _ValueType(*__ifirst);
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
} catch (...) {
_VSTD::__destroy(__ofirst, __idx);
throw;
}
#endif
return pair<_InputIterator, _ForwardIterator>(_VSTD::move(__ifirst), _VSTD::move(__idx));
}
```
Given that `++__ifirst` can fail in `for (; __n > 0 && __idx != __olast; ++__ifirst, (void)++__idx, (void)--__n)`; and `_VSTD::__destroy(__ofirst, __idx);` is how the destruction of already-constructed objects is handled in the case of an exception, it seems to be that the order of `++__ifirst` and `(void)++__idx` must be swapped, like:
`for (; __n > 0 && __idx != __olast; (void)++__idx, ++__ifirst, (void)--__n)`.
I tried this solution. It does solve the issue, i.e. `assert(obj.use_count() == 1);`, in the original program, succeeds.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJy8WFuP4joS_jXmxQIFZxrIAw9cmj1IZ8-Mdlrn7Ftkkgp4xrEj2-ke-tevyklIAqEv87AIQceuy-evylXVcGvFUQEsycOaPGxHvHQnbZarn9yeCgP56KDT8_LpJCwVlnJqeV5IoPCr-naaOiOORzDUVTK2BBKuSLAlQfM5C-p39chCoRJZpkBJuEm4tWAcCR-HdnPItTnf2bTOCHVsN_2ng7yQ3HkBdy5A8Rzo00XIOlMmju4dGO60oZlQXNb683X1By2tUEf6zGUJMdqgJNyijWaf0lokFVkGBlTSkbMuxfOHq8IZ3I9dq1hpGaiVvHzHTaKVdZSw2YCrQgvlwNxTWQ2oiPqQccIdHLU59_EJVZQuvgg5frzYqC01LBG2aG3j62LDileIHRUqhV-Ebd6Wgl8JFE5oFQ_LP7UE6MMPwqL-NglXtaP5uv7eEra5MTtf36x4ObQ5X_vPbWu5jbp_2A4S4CEVDRlr_170APbM4Atvw5iEjx4BRY1wS6e9KOFLZJSwxZVw-IjCzeINbde8XLuu3Bv90kbgYgOF59sbGD1K8GXAlUZhYiGMnvxdkm4YEsq9TdJFFW-tz84bhx26qx0W3aCvwaKRN5F60ebutWhX7wazcVBHBHPoLT_D16Or8d7V6Mo-0a6_bvSqT6EczblQ3VP0TlBVg73aO0_x5VZjBW1gnLiBNC5cf_VSYPHdschLpxFVv6Dk_CfElaVhKwiQ_QFSavqPNjIljF2HswV-TZF13Li4uSFbGtykwTuk0qsbeCOu0o4guzHvCawqlMdC5usOpqFK1BSd2_v2UeLxPhT3IdvX-5adAMIWBYKwr4RF_VgdwcV4X7Th5hwfSuxjn0wItmgZG9MeFbdXtO7ybGFfEQhi-Yi2M-cPFLsLvlIJJZzgUrxCGie6OMd4Lapw1URsaNFzcVP6Eu6SE1blyWTyfrG9aibNw-W8-vBjUlqIE11iNVzUkahysYOjrSFX05L_fDoBJbPgM1ZnAeWOuhMg0XjsjAtp29Gs72blLQutvBheYCy97_pBs6VKvGJVgzY0EzgXgnWTpPDpJ4UCOg8mladXe0Lj_KCNoxPCdnyiSzcI6gAJL60_-QAQPCGOpIkruZRnlGJk1rhpeUt0XggJhmaSHy3d09L68w26TCRXx6rn0LHS1qVCJb1nKQ7Ns7Bn6yCnUj7n48LoH5C4sVDWcSkJ25XWELaTOuH4VA-uhO2Spqftnqd0_OfH1KU40PE_krDN2BTcnQjbfFxPXo5wDNvQ3E-3PX3hlvJDM-Dz5KfPJAPcYopgxz5BNeyjxAkM3GO0O5AnkltL479xfn06F4DZUa_tcRptB87L-nfx2hXbafPCTTokCMoJBfIy6gvlEy_-c7_efPsW_7HfPsa7_3z9d7xa72nBBVa2W7c3HhpzcXynuvRt0DgWmTDW1xuPnsYotrktW53XtVcax7pjpT4arkqOi1G3EA0oi7TqYhczlzLDQpGpFLKWl9X3-K-v8eN_N4_fnvZf__remHX4D8O81QOViqw9RebH4gUJ13hAHFpp4P93YbMGAJvWGDzocF0Pc12GCFs8a5H6WlLvpb_6G-OxJ7BXiKtyr-AF5eK_vz9tq5U4RiWRnauBsbYWIWHdrKv3ahTR77BD5tvrTtGJSQ9SCtYZjZB6UW2wdVulH9s7K21X6PFfb9dj6adyuUdXrp_Bw2qpQLUhgYbHDzasf4lnwIGZO6zLV3GfBTThVaehQqHA_z-ZZjg_Uq5SdP_JcFWN56RffBX08mXVA3VGuTTA0_PYT4q4DilOgZA4_wvKiatUQornRuWEW_Baqp0e0Z1w1ALkFqvrASoiUV6bFAwqDLJaH2eYh1lA89I6NGdfeFFAWjXnn73S_XuRuMv7_RB1IjHp9x5nBKTVz0lWyxIpmdC9o6kGv_IMbffxXE1g8vnpyGuqmlRxFIpLWhh9NDz3o2KZJACprbGN0mWYRmHER7CczubRYhaxaTQ6LadRMIuyh4SlhyhbBBEL04eHKVtkYbBY8C9sJJYsYGEQshljLJhGkwxgzubhwzRih3k2S8mXAHIu5AT7-USb48ifbDmbzqeLkeQHkNb_OscYFrz62Iw8bEdm6WeAQ3m05EsghXW2teKEk7B8czauKFW6SjbfyX16trloaaKNgcTJ86g0cnlyrvBDJNsRtjsKdyoPk0TnOGnI5-armUpw7EG4lrCdP87_AgAA__-A8vZT">