libstdc++: fix a dangling reference crash in ranges::is_permutation [PR118160]

The code was caching the result of `invoke(proj, *it)` in a local
`auto &&` variable. The problem is that this may create dangling
references, for instance in case `proj` is `std::identity` (the common
case) and `*it` produces a prvalue: lifetime extension does not
apply here due to the expressions involved.

Instead, store (and lifetime-extend) the result of `*it` in a separate
variable, then project that variable. While at it, also forward the
result of the projection to the predicate, so that the predicate can
act on the proper value category.

libstdc++-v3/ChangeLog:

	PR libstdc++/118160
	PR libstdc++/100249
	* include/bits/ranges_algo.h (__is_permutation_fn): Avoid a
	dangling reference by storing the result of the iterator
	dereference and the result of the projection in two distinct
	variables, in order to lifetime-extend each one.
	Forward the projected value to the predicate.
	* testsuite/25_algorithms/is_permutation/constrained.cc: Add a
	test with a range returning prvalues. Test it in a constexpr
	context, in order to rely on the compiler to catch UB.

Signed-off-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
This commit is contained in:
Giuseppe D'Angelo 2025-02-06 14:24:17 +00:00 committed by Jonathan Wakely
parent 6e758f378a
commit 2a2bd96d0d
No known key found for this signature in database
2 changed files with 18 additions and 2 deletions

View file

@ -567,9 +567,12 @@ namespace ranges
for (auto __scan = __first1; __scan != __last1; ++__scan)
{
auto&& __proj_scan = std::__invoke(__proj1, *__scan);
auto&& __scan_deref = *__scan;
auto&& __proj_scan =
std::__invoke(__proj1, std::forward<decltype(__scan_deref)>(__scan_deref));
auto __comp_scan = [&] <typename _Tp> (_Tp&& __arg) -> bool {
return std::__invoke(__pred, __proj_scan,
return std::__invoke(__pred,
std::forward<decltype(__proj_scan)>(__proj_scan),
std::forward<_Tp>(__arg));
};
if (__scan != ranges::find_if(__first1, __scan,

View file

@ -19,6 +19,7 @@
#include <algorithm>
#include <iterator>
#include <ranges>
#include <testsuite_hooks.h>
#include <testsuite_iterators.h>
@ -76,10 +77,22 @@ test03()
while (std::next_permutation(std::begin(cx), std::end(cx)));
}
constexpr
bool
test04() // PR118160, do not create dangling references
{
int x[] = { 4, 3, 2, 1 };
auto y = std::views::iota(1, 5);
return ranges::is_permutation(x, y) && ranges::is_permutation(y, x);
}
static_assert(test04());
int
main()
{
test01();
test02();
test03();
VERIFY( test04() );
}