Quuxplusone / LLVMBugzillaTest

0 stars 0 forks source link

evaluation of constraints for std::ranges::views::drop() fails unnecessarily #46478

Open Quuxplusone opened 3 years ago

Quuxplusone commented 3 years ago
Bugzilla Link PR47509
Status NEW
Importance P normal
Reported by Avi Kivity (avi@scylladb.com)
Reported on 2020-09-13 07:35:14 -0700
Last modified on 2021-11-23 09:04:52 -0800
Version trunk
Hardware PC Linux
CC blitzrakete@gmail.com, erik.pilkington@gmail.com, johelegp@gmail.com, llvm-bugs@lists.llvm.org, mikhail.matrosov@gmail.com, rafael@espindo.la, raffael@casagrande.ch, richard-llvm@metafoo.co.uk, victor@westerhu.is, zilla@kayari.org
Fixed by commit(s)
Attachments
Blocks
Blocked by
See also
The test program

==== begin test program ====
#include <vector>
#include <ranges>

void
f(std::vector<int> vec) {
    for (auto& e : vec | std::ranges::views::drop(1)) {
    }
}
==== end test program ====

Does not compile on clang, either with libc++ or libstdc++, failing on some
enormously complicated constraints. gcc accepts it.
Quuxplusone commented 3 years ago
I narrowed it down to eager evaluation of constraints:

1. view_interface<iota_view> is instantiated as a base class of iota_view.
Clearly iota_view isn't defined at this stage, it's just a forward-declared
name.

2. view_interface instantiates iterator_t, which is an alias to
std::__detail::__range_iter_t.

3. __range_iter_t instantiates __ranges_begin.

4. __ranges_begin requires that its template parameter is a __member_begin<>
(among other options, but this is the valid one here).

5. __member_begin requires that the type (iota_view) have a begin() function.
But the type isn't defined yet.

According to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97120#c1, evaluation
should be lazy.
Quuxplusone commented 3 years ago
This reduces to something interesting:

Clang accepts:
----------------------------------------------
template <typename _Tp>
concept __member_begin = requires(_Tp __t) {
    {__t.begin()};
};
template <typename _Tp>
concept nothing = requires(_Tp __t) {
    {42};
};
template <typename _Tp>
requires __member_begin<_Tp> void __ranges_begin() {}
template <typename _Derived>
struct view_interface {
    void foo() requires __member_begin<_Derived> {}
//    void bar() requires nothing<decltype(__ranges_begin<_Derived>())> {}
};
struct drop_view : public view_interface<drop_view> {};
------------------------------------------------

Uncomment the declaration of bar and clang rejects this. gcc is OK with both.

If I understand it correctly, both gcc and clang agree that the direct use of a
unsatisfied requires should just disable foo. The disagreement is about an
indirect one, like in bar(). With gcc bar() is just disabled, but clang
produces an error. Note that gcc rejects:

-------------------------------------------------
template <typename _Tp>
concept __member_begin = requires(_Tp __t) {
    {__t.begin()};
};
template <typename _Tp>
concept nothing = requires(_Tp __t) {
    {42};
};
template <typename _Tp>
requires __member_begin<_Tp> void __ranges_begin() {}
template <typename _Derived>
struct view_interface {
    void foo() requires __member_begin<_Derived> {}
    void bar() requires nothing<decltype(__ranges_begin<_Derived>())> {}
};
struct drop_view : public view_interface<drop_view> {
    void zed() {
        this->bar();
    }
};
-------------------------------------------------
Quuxplusone commented 2 years ago

This is indeed clang's bug: https://bugs.llvm.org/show_bug.cgi?id=50864

Quuxplusone commented 2 years ago
(In reply to Mikhail Matrosov from comment #3)
> This is indeed clang's bug: https://bugs.llvm.org/show_bug.cgi?id=50864

And this one and that are both dups of Bug 44833