Potential bug in use of auto in comparator lambda for std::upper_bound when debug build is enabled

I’m working on some code that compares two different types with std::upper_bound and does so with a custom lambda, which takes both parameters as type “auto”. When building normally, this compiles fine. When building with -D_LIBCPP_DEBUG=1, this fails and somewhere in the template magic, libc++ seems to confuse the two types and pass them into the lambda backwards.

Here is some minimal code to reproduce, and the corresponding compilation error:

#include
#include

struct T1 {
int i;
};

int main(int argc, char const* argv[]) {
std::vector t1_vec = {{.i = 1}, {.i = 3}};

std::upper_bound(t1_vec.begin(), t1_vec.end(), 2,
[](const auto& a, const auto& b) { return a < b.i; });
return 0;
}

experimental/upper_bound.cpp:14:67: error: member reference base type

‘const int’ is not a structure or union

[](const auto& a, const auto& b) { return a < b.i; });

~^~

external/llvm_toolchain/bin/…/include/c++/v1/algorithm:777:20: note: in instantiation of function template specialization ‘main(int, const char **)::(anonymous class)::operator()<T1, int>’ requested here

decltype((void)_VSTD::declval<_Compare&>()(

^

external/llvm_toolchain/bin/…/include/c++/v1/__config:473:15: note: expanded from macro ‘_VSTD’

#define _VSTD std::_LIBCPP_NAMESPACE

^

external/llvm_toolchain/bin/…/include/c++/v1/algorithm:771:13: note: while substituting deduced template arguments into function template ‘__do_compare_assert’ [with _LHS = T1, _RHS = int]

__do_compare_assert(0, __y, __x);

^

external/llvm_toolchain/bin/…/include/c++/v1/algorithm:4249:13: note: in instantiation of function template specialization ‘std::__1::__debug_less<(lambda at experimental/upper_bound.cpp:14:20)>::operator()<int, T1>’ requested here

if (__comp(_value, *__m))

^

external/llvm_toolchain/bin/…/include/c++/v1/algorithm:4268:12: note: in instantiation of function template specialization ‘std::__1::__upper_bound<std::__1::__debug_less<(lambda at experimental/upper_bound.cpp:14:20)> &, std::__1::__wrap_iter<T1 *>, int>’ requested here

return __upper_bound<_Comp_ref>(__first, __last, _value, __c);

^

experimental/upper_bound.cpp:13:8: note: in instantiation of function template specialization ‘std::__1::upper_bound<std::__1::__wrap_iter<T1 *>, int, (lambda at experimental/upper_bound.cpp:14:20)>’ requested here

std::upper_bound(t1_vec.begin(), t1_vec.end(), 2,

I just tried this with libc++ trunk, and didn’t get a compile error.
(I got a link error, but that’s because I wasn’t linking against a debug dylib)

What version of libc++ are you using?

– Marshall

The code referenced in the diagnostics appears in 7.0 but not 8.0.
At some point the debug implementation was removed from upper and lower bound,
but I can’t find the exact commit that removed it.

Either way this should be fixed now.

/Eric

That’s correct. I was using the 7.0.0 release. Good to know it’s fixed in 8.0. Thanks for investigating!

https://github.com/llvm-mirror/libcxx/commit/293b83d6e64b58b0e36c952b04302a0e4427e250#diff-2fe689a5b4ef8c00d7e2149538b0153b

– Marshall