Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<algorithm>: ranges::inplace_merge doesn't seem to be able to utilize ranges::upper_bound #4863

Closed
hewillk opened this issue Jul 25, 2024 · 8 comments · Fixed by #4927
Closed
Labels
bug Something isn't working fixed Something works now, yay! ranges C++20/23 ranges

Kommentare

@hewillk
Copy link
Contributor

hewillk commented Jul 25, 2024

#include <algorithm>
#include <vector>

int main() {
  std::vector<int> v;
  auto cmp = [](int&, int&) { return true; };
  std::ranges::sort(v, cmp);
  std::ranges::inplace_merge(v, v.begin(), cmp); // hard error
}

https://godbolt.org/z/soe8sa75q

The above code is rejected by libstdc++, libc++, and MSVC-STL, which shouldn't be because ranges::inplace_merge has the same constraint signature as ranges::sort.
The root cause is that inplace_merge uses upper_bound, which then requires Compare to satisfy indirect_strict_weak_order<const T*, I> (note the const here), which is not guaranteed by the constraints of inplace_merge.

@hewillk hewillk changed the title <algorithm>: ranges::implace_merge doesn't seem to be able to utilize ranges::upper_bound <algorithm>: ranges:: inplace_merge doesn't seem to be able to utilize ranges::upper_bound Jul 25, 2024
@hewillk hewillk changed the title <algorithm>: ranges:: inplace_merge doesn't seem to be able to utilize ranges::upper_bound <algorithm>: ranges::inplace_merge doesn't seem to be able to utilize ranges::upper_bound Jul 25, 2024
@hewillk hewillk changed the title <algorithm>: ranges::inplace_merge doesn't seem to be able to utilize ranges::upper_bound <algorithm>: ranges::inplace_merge doesn't seem to be able to utilize ranges::upper_bound Jul 25, 2024
@frederick-vs-ja
Copy link
Contributor

The root cause is that inplace_merge uses upper_bound, which then requires Compare to satisfy indirect_strict_weak_order<const T*, I> (note the const here), which is not guaranteed by the constraints of inplace_merge.

Pedantically wrong for all three major implementations. Implementations actually call the unconstrained (by possible static_assert-ing) internal versions instead of ranges::upper_bound. Although this perfectly indicates that the partial implementation posted on cppreference is currently incorrect.

At the first glance, I thought we should remove const somewhere. It should be investigated whether additional fix is needed for proxy references.

@hewillk
Copy link
Contributor Author

hewillk commented Jul 26, 2024

At the first glance, I thought we should remove const somewhere. It should be investigated whether additional fix is needed for proxy references.

Not sure if making _Upper_bound_unchecked/_Lower_bound_unchecked accept _Ty&& _Val instead of const _Ty& _Val and doing the forwarding internally would fix this.

@StephanTLavavej StephanTLavavej added LWG issue needed A wording defect that should be submitted to LWG as a new issue ranges C++20/23 ranges labels Jul 31, 2024
@StephanTLavavej
Copy link
Member

@barcharcraz and I talked about this at the weekly maintainer meeting. The fact that the Majestic Three implementations all reject this code, and the fact that it seems super pathological/useless for comparison function objects to reject const arguments, indicates that we need to send this to the LWG and they need to decide what should happen here. Our initial reaction is that the sortable concept should require const-comparisons to work, in the same way that ranges::upper_bound is explicitly adding constness when checking for comparability, but mostly we just want the LWG to tell us exactly what should happen here.

@hewillk
Copy link
Contributor Author

hewillk commented Aug 1, 2024

@barcharcraz and I talked about this at the weekly maintainer meeting. The fact that the Majestic Three implementations all reject this code, and the fact that it seems super pathological/useless for comparison function objects to reject const arguments, indicates that we need to send this to the LWG and they need to decide what should happen here. Our initial reaction is that the sortable concept should require const-comparisons to work, in the same way that ranges::upper_bound is explicitly adding constness when checking for comparability, but mostly we just want the LWG to tell us exactly what should happen here.

OK. Let me change the example that is only rejected by MSVC-STL:

#include <algorithm>
#include <vector>

struct S {
  operator int();
};

int main() {
  std::vector<int> v;
  auto cmp = [](const int&, const int&) { return true; };
  std::ranges::inplace_merge(v, v.begin(), cmp, [](int) { return S{}; });
}

https://godbolt.org/z/M1z8aaq9s

Could this be considered a library bug?

@StephanTLavavej StephanTLavavej added bug Something isn't working and removed LWG issue needed A wording defect that should be submitted to LWG as a new issue labels Aug 1, 2024
@StephanTLavavej
Copy link
Member

Yeah, that looks more like a bug to me, since MSVC is alone there (with the nitpicks that a "do nothing" comparator really ought to return false, and that it's confusing for the range to be ints that are projected to S and then compared as int - it would be clearer if 3 different types were involved).

@frederick-vs-ja
Copy link
Contributor

For the LWG issue part, LWG-3031 seems roughly related.

@hewillk
Copy link
Contributor Author

hewillk commented Aug 3, 2024

For the LWG issue part, LWG-3031 seems roughly related.

Quoted from LWG-3031:

Similar to the Ranges TS design, the P/R below requires Predicate, BinaryPredicate, and Compare to accept all mixes of const and non-const arguments.

Doesn't this already indicate that this is a bug in the standard library implementation rather than a defect in the standard?
The LWG considers these examples should work.

@frederick-vs-ja
Copy link
Contributor

Quoted from LWG-3031:

Similar to the Ranges TS design, the P/R below requires Predicate, BinaryPredicate, and Compare to accept all mixes of const and non-const arguments.

Doesn't this already indicate that this is a bug in the standard library implementation rather than a defect in the standard? The LWG considers these examples should work.

I didn't know what the paragraph intended to mean. Sortable in Ranges TS wasn't fundamentally different from the current std::sortable and accepted non-const-only comparators. But the decision in that paragraph and the final resolution of LWG-3031 imposed stricter requirements on Compare - which was, IIUC, inconsistent with Ranges TS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working fixed Something works now, yay! ranges C++20/23 ranges
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants