[clang] Incorrect -Wunneeded-member-function warning in C++20 mode with ranges library code
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
This program triggers a -Wunneeded-member-function warning if define INCLUDE_EQUALS_OPERATOR is set (see https://godbolt.org/z/17Tneax4K):
```c++
#include
#include
#include
namespace {
#define INCLUDE_EQUALS_OPERATOR
// The struct is intentionally not declared in any header. It is local to this translation unit.
struct Foo
{
std::strong_ordering operator<=>(const Foo& p_rhs) const; // not defaulted
#if defined(INCLUDE_EQUALS_OPERATOR)
bool operator==(const Foo& p_rhs) const; // not defaulted
#endif
int m_value;
};
std::strong_ordering Foo::operator<=>(const Foo& p_rhs) const
{
return m_value <=> p_rhs.m_value;
}
#if defined(INCLUDE_EQUALS_OPERATOR)
bool Foo::operator==(const Foo& p_rhs) const
{
return m_value == p_rhs.m_value;
}
#endif
} // namespace
int main()
{
std::vector v;
std::ranges::sort(v); // requires operator==() through concept satisfaction
return 0;
}
```
The compiler deduces that operator==() is not used and therefore not needed:
```shell
:26:11: warning: member function 'operator==' is not needed and will not be emitted [-Wunneeded-member-function]
26 | bool Foo::operator==(const Foo& p_rhs) const
| ^~~~~~~~
1 warning generated.
```
However, if the define INCLUDE_EQUALS_OPERATOR is commented out (see https://godbolt.org/z/ooYxqa1fM) then the warning transforms into an error:
```
:37:5: error: no matching function for call to object of type 'const __sort_fn'
37 | std::ranges::sort(v); // requires operator==() through concept satisfaction
| ^~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/17.0.0/../../../../include/c++/17.0.0/bits/ranges_algo.h:2507:7: note: candidate template ignored: constraints not satisfied [with _Range = std::vector &, _Comp = ranges::less, _Proj = identity]
2507 | operator()(_Range&& __r, _Comp __comp = {}, _Proj __proj = {}) const
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/17.0.0/../../../../include/c++/17.0.0/bits/ranges_algo.h:2505:16: note: because 'sortable > &>, std::ranges::less, std::identity>' evaluated to false
2505 | requires sortable, _Comp, _Proj>
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/17.0.0/../../../../include/c++/17.0.0/bits/iterator_concepts.h:971:10: note: because 'indirect_strict_weak_order > >, std::identity>>' evaluated to false
971 | && indirect_strict_weak_order<_Rel, projected<_Iter, _Proj>>;
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/17.0.0/../../../../include/c++/17.0.0/bits/iterator_concepts.h:755:10: note: because 'strict_weak_order > >, std::identity>::__type>, __indirect_value_t > >, std::identity>::__type>>' evaluated to false
755 | && strict_weak_order<_Fn&, __indirect_value_t<_I1>, __indirect_value_t<_I2>>
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/17.0.0/../../../../include/c++/17.0.0/concepts:406:33: note: because 'relation' evaluated to false
406 | concept strict_weak_order = relation<_Rel, _Tp, _Up>;
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/17.0.0/../../../../include/c++/17.0.0/concepts:397:9: note: because 'predicate' evaluated to false
397 | = predicate<_Rel, _Tp, _Tp> && predicate<_Rel, _Up, _Up>
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/17.0.0/../../../../include/c++/17.0.0/concepts:391:25: note: because 'regular_invocable' evaluated to false
391 | concept predicate = regular_invocable<_Fn, _Args...>
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/17.0.0/../../../../include/c++/17.0.0/concepts:387:33: note: because 'invocable' evaluated to false
387 | concept regular_invocable = invocable<_Fn, _Args...>;
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/17.0.0/../../../../include/c++/17.0.0/concepts:383:25: note: because 'is_invocable_v' evaluated to false
383 | concept invocable = is_invocable_v<_Fn, _Args...>;
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/17.0.0/../../../../include/c++/17.0.0/bits/ranges_algo.h:2483:7: note: candidate function template not viable: requires at least 2 arguments, but 1 was provided
2483 | operator()(_Iter __first, _Sent __last,
| ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2484 | _Comp __comp = {}, _Proj __proj = {}) const
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
```
The explanation seems to be that std::ranges::sort(v) by default uses std::ranges::less which requires (via a C++20 concept) std::totally_ordered_with and this in turn demands std::equality_comparable. So according to the concept restrictions operator==() is needed, but the warning does not seem to be considering this fact. It only focuses on operator==() being unused in the code itself, which is true.
Contributor guide
Research direction
Start by compiling the provided C++20 reproducer with Clang and -Wunneeded-member-function, then trace the warning's analysis of std::ranges::sort, std::ranges::less, and the equality_comparable concepts. Done means operator== is not falsely reported as unneeded when concept satisfaction requires it, while genuinely unused member functions still receive the warning.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100