-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[libc++][ranges] Adjust inheritance detection for enable_view
#132582
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
[libc++][ranges] Adjust inheritance detection for enable_view
#132582
Conversation
Per [range.view]/6, a `view_interface` isn't a base class of itself, so `enable_view` should report `false`. Also, current implementation strategy handles `const` but not `volatile`, IIUC cv-qualifiers should be consistent handled. In `enable_view.compile.pass.cpp`, coverage for (`const`) `volatile` types are added. Drive-by: Remove one unnessary `test_macro.h` inclusion in a test.
@llvm/pr-subscribers-libcxx Author: A. Jiang (frederick-vs-ja) ChangesPer [range.view]/6, a In Drive-by: Remove one unnessary Fixes #132577. Full diff: https://github.com/llvm/llvm-project/pull/132582.diff 3 Files Affected:
diff --git a/libcxx/include/__ranges/enable_view.h b/libcxx/include/__ranges/enable_view.h
index f570926eb67c3..6dee6ea3fa3d2 100644
--- a/libcxx/include/__ranges/enable_view.h
+++ b/libcxx/include/__ranges/enable_view.h
@@ -14,7 +14,6 @@
#include <__concepts/same_as.h>
#include <__config>
#include <__type_traits/is_class.h>
-#include <__type_traits/is_convertible.h>
#include <__type_traits/remove_cv.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -34,12 +33,12 @@ template <class _Derived>
class view_interface;
template <class _Op, class _Yp>
- requires is_convertible_v<_Op*, view_interface<_Yp>*>
-void __is_derived_from_view_interface(const _Op*, const view_interface<_Yp>*);
+ requires(!same_as<_Op, view_interface<_Yp>>)
+void __is_derived_from_view_interface(view_interface<_Yp>*);
template <class _Tp>
inline constexpr bool enable_view = derived_from<_Tp, view_base> || requires {
- ranges::__is_derived_from_view_interface((_Tp*)nullptr, (_Tp*)nullptr);
+ ranges::__is_derived_from_view_interface<remove_cv_t<_Tp>>((remove_cv_t<_Tp>*)nullptr);
};
} // namespace ranges
diff --git a/libcxx/test/std/ranges/range.req/range.view/enable_view.compile.pass.cpp b/libcxx/test/std/ranges/range.req/range.view/enable_view.compile.pass.cpp
index 6412234dce048..202b5bba451ce 100644
--- a/libcxx/test/std/ranges/range.req/range.view/enable_view.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.req/range.view/enable_view.compile.pass.cpp
@@ -25,6 +25,12 @@ static_assert(!std::ranges::enable_view<Empty&&>);
static_assert(!std::ranges::enable_view<const Empty>);
static_assert(!std::ranges::enable_view<const Empty&>);
static_assert(!std::ranges::enable_view<const Empty&&>);
+static_assert(!std::ranges::enable_view<volatile Empty>);
+static_assert(!std::ranges::enable_view<volatile Empty&>);
+static_assert(!std::ranges::enable_view<volatile Empty&&>);
+static_assert(!std::ranges::enable_view<const volatile Empty>);
+static_assert(!std::ranges::enable_view<const volatile Empty&>);
+static_assert(!std::ranges::enable_view<const volatile Empty&&>);
// Derives from view_base, but privately
struct PrivateViewBase : private std::ranges::view_base { };
@@ -34,6 +40,12 @@ static_assert(!std::ranges::enable_view<PrivateViewBase&&>);
static_assert(!std::ranges::enable_view<const PrivateViewBase>);
static_assert(!std::ranges::enable_view<const PrivateViewBase&>);
static_assert(!std::ranges::enable_view<const PrivateViewBase&&>);
+static_assert(!std::ranges::enable_view<volatile PrivateViewBase>);
+static_assert(!std::ranges::enable_view<volatile PrivateViewBase&>);
+static_assert(!std::ranges::enable_view<volatile PrivateViewBase&&>);
+static_assert(!std::ranges::enable_view<const volatile PrivateViewBase>);
+static_assert(!std::ranges::enable_view<const volatile PrivateViewBase&>);
+static_assert(!std::ranges::enable_view<const volatile PrivateViewBase&&>);
// Derives from view_base, but specializes enable_view to false
struct EnableViewFalse : std::ranges::view_base { };
@@ -44,6 +56,12 @@ static_assert(!std::ranges::enable_view<EnableViewFalse&&>);
static_assert(std::ranges::enable_view<const EnableViewFalse>);
static_assert(!std::ranges::enable_view<const EnableViewFalse&>);
static_assert(!std::ranges::enable_view<const EnableViewFalse&&>);
+static_assert(std::ranges::enable_view<volatile EnableViewFalse>);
+static_assert(!std::ranges::enable_view<volatile EnableViewFalse&>);
+static_assert(!std::ranges::enable_view<volatile EnableViewFalse&&>);
+static_assert(std::ranges::enable_view<const volatile EnableViewFalse>);
+static_assert(!std::ranges::enable_view<const volatile EnableViewFalse&>);
+static_assert(!std::ranges::enable_view<const volatile EnableViewFalse&&>);
// Derives from view_base
struct PublicViewBase : std::ranges::view_base { };
@@ -53,6 +71,12 @@ static_assert(!std::ranges::enable_view<PublicViewBase&&>);
static_assert(std::ranges::enable_view<const PublicViewBase>);
static_assert(!std::ranges::enable_view<const PublicViewBase&>);
static_assert(!std::ranges::enable_view<const PublicViewBase&&>);
+static_assert(std::ranges::enable_view<volatile PublicViewBase>);
+static_assert(!std::ranges::enable_view<volatile PublicViewBase&>);
+static_assert(!std::ranges::enable_view<volatile PublicViewBase&&>);
+static_assert(std::ranges::enable_view<const volatile PublicViewBase>);
+static_assert(!std::ranges::enable_view<const volatile PublicViewBase&>);
+static_assert(!std::ranges::enable_view<const volatile PublicViewBase&&>);
// Does not derive from view_base, but specializes enable_view to true
struct EnableViewTrue { };
@@ -63,6 +87,12 @@ static_assert(!std::ranges::enable_view<EnableViewTrue&&>);
static_assert(!std::ranges::enable_view<const EnableViewTrue>);
static_assert(!std::ranges::enable_view<const EnableViewTrue&>);
static_assert(!std::ranges::enable_view<const EnableViewTrue&&>);
+static_assert(!std::ranges::enable_view<volatile EnableViewTrue>);
+static_assert(!std::ranges::enable_view<volatile EnableViewTrue&>);
+static_assert(!std::ranges::enable_view<volatile EnableViewTrue&&>);
+static_assert(!std::ranges::enable_view<const volatile EnableViewTrue>);
+static_assert(!std::ranges::enable_view<const volatile EnableViewTrue&>);
+static_assert(!std::ranges::enable_view<const volatile EnableViewTrue&&>);
// Make sure that enable_view is a bool, not some other contextually-convertible-to-bool type.
ASSERT_SAME_TYPE(decltype(std::ranges::enable_view<Empty>), const bool);
@@ -75,6 +105,12 @@ static_assert(!std::ranges::enable_view<V1&&>);
static_assert(std::ranges::enable_view<const V1>);
static_assert(!std::ranges::enable_view<const V1&>);
static_assert(!std::ranges::enable_view<const V1&&>);
+static_assert(std::ranges::enable_view<volatile V1>);
+static_assert(!std::ranges::enable_view<volatile V1&>);
+static_assert(!std::ranges::enable_view<volatile V1&&>);
+static_assert(std::ranges::enable_view<const volatile V1>);
+static_assert(!std::ranges::enable_view<const volatile V1&>);
+static_assert(!std::ranges::enable_view<const volatile V1&&>);
struct V2 : std::ranges::view_interface<V1>, std::ranges::view_interface<V2> {};
static_assert(!std::ranges::enable_view<V2>);
@@ -83,6 +119,12 @@ static_assert(!std::ranges::enable_view<V2&&>);
static_assert(!std::ranges::enable_view<const V2>);
static_assert(!std::ranges::enable_view<const V2&>);
static_assert(!std::ranges::enable_view<const V2&&>);
+static_assert(!std::ranges::enable_view<volatile V2>);
+static_assert(!std::ranges::enable_view<volatile V2&>);
+static_assert(!std::ranges::enable_view<volatile V2&&>);
+static_assert(!std::ranges::enable_view<const volatile V2>);
+static_assert(!std::ranges::enable_view<const volatile V2&>);
+static_assert(!std::ranges::enable_view<const volatile V2&&>);
struct V3 : std::ranges::view_interface<V1> {};
static_assert(std::ranges::enable_view<V3>);
@@ -91,13 +133,35 @@ static_assert(!std::ranges::enable_view<V3&&>);
static_assert(std::ranges::enable_view<const V3>);
static_assert(!std::ranges::enable_view<const V3&>);
static_assert(!std::ranges::enable_view<const V3&&>);
+static_assert(std::ranges::enable_view<volatile V3>);
+static_assert(!std::ranges::enable_view<volatile V3&>);
+static_assert(!std::ranges::enable_view<volatile V3&&>);
+static_assert(std::ranges::enable_view<const volatile V3>);
+static_assert(!std::ranges::enable_view<const volatile V3&>);
+static_assert(!std::ranges::enable_view<const volatile V3&&>);
struct PrivateInherit : private std::ranges::view_interface<PrivateInherit> {};
static_assert(!std::ranges::enable_view<PrivateInherit>);
+static_assert(!std::ranges::enable_view<const PrivateInherit>);
+static_assert(!std::ranges::enable_view<volatile PrivateInherit>);
+static_assert(!std::ranges::enable_view<const volatile PrivateInherit>);
+
+// https://github.com/llvm/llvm-project/issues/132577
+// enable_view<view_interface<T>> should be false.
+static_assert(!std::ranges::enable_view<std::ranges::view_interface<V1>>);
+static_assert(!std::ranges::enable_view<const std::ranges::view_interface<V1>>);
+static_assert(!std::ranges::enable_view<volatile std::ranges::view_interface<V1>>);
+static_assert(!std::ranges::enable_view<const volatile std::ranges::view_interface<V1>>);
// ADL-proof
struct Incomplete;
template<class T> struct Holder { T t; };
static_assert(!std::ranges::enable_view<Holder<Incomplete>*>);
+static_assert(!std::ranges::enable_view<const Holder<Incomplete>*>);
+static_assert(!std::ranges::enable_view<volatile Holder<Incomplete>*>);
+static_assert(!std::ranges::enable_view<const volatile Holder<Incomplete>*>);
static_assert(!std::ranges::enable_view<void>);
+static_assert(!std::ranges::enable_view<const void>);
+static_assert(!std::ranges::enable_view<volatile void>);
+static_assert(!std::ranges::enable_view<const volatile void>);
diff --git a/libcxx/test/std/ranges/range.req/range.view/view.compile.pass.cpp b/libcxx/test/std/ranges/range.req/range.view/view.compile.pass.cpp
index e7a7f30cb20fb..38828cbe46b5b 100644
--- a/libcxx/test/std/ranges/range.req/range.view/view.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.req/range.view/view.compile.pass.cpp
@@ -15,8 +15,6 @@
#include <ranges>
-#include "test_macros.h"
-
// The type would be a view, but it's not moveable.
struct NotMoveable : std::ranges::view_base {
NotMoveable() = default;
@@ -90,3 +88,98 @@ static_assert(std::movable<View>);
static_assert(std::default_initializable<View>);
static_assert(std::ranges::enable_view<View>);
static_assert(std::ranges::view<View>);
+
+// const view types
+
+struct ConstView1 : std::ranges::view_base {
+ ConstView1(const ConstView1&&);
+ const ConstView1& operator=(const ConstView1&&) const;
+
+ friend void swap(const ConstView1&, const ConstView1&);
+
+ friend int* begin(const ConstView1&);
+ friend int* end(const ConstView1&);
+};
+static_assert(std::ranges::range<const ConstView1>);
+static_assert(std::movable<const ConstView1>);
+static_assert(!std::default_initializable<const ConstView1>);
+static_assert(std::ranges::enable_view<const ConstView1>);
+static_assert(std::ranges::view<const ConstView1>);
+
+struct ConstView2 : std::ranges::view_interface<ConstView2> {
+ ConstView2(const ConstView2&&);
+ const ConstView2& operator=(const ConstView2&&) const;
+
+ friend void swap(const ConstView2&, const ConstView2&);
+
+ friend int* begin(const ConstView2&);
+ friend int* end(const ConstView2&);
+};
+static_assert(std::ranges::range<const ConstView2>);
+static_assert(std::movable<const ConstView2>);
+static_assert(!std::default_initializable<const ConstView2>);
+static_assert(std::ranges::enable_view<const ConstView2>);
+static_assert(std::ranges::view<const ConstView2>);
+
+// volatile view types
+struct VolatileView1 : std::ranges::view_base {
+ VolatileView1(volatile VolatileView1&&);
+ volatile VolatileView1& operator=(volatile VolatileView1&&) volatile;
+
+ friend void swap(volatile VolatileView1&, volatile VolatileView1&);
+
+ friend int* begin(volatile VolatileView1&);
+ friend int* end(volatile VolatileView1&);
+};
+static_assert(std::ranges::range<volatile VolatileView1>);
+static_assert(std::movable<volatile VolatileView1>);
+static_assert(!std::default_initializable<volatile VolatileView1>);
+static_assert(std::ranges::enable_view<volatile VolatileView1>);
+static_assert(std::ranges::view<volatile VolatileView1>);
+
+struct VolatileView2 : std::ranges::view_interface<VolatileView2> {
+ VolatileView2(volatile VolatileView2&&);
+ volatile VolatileView2& operator=(volatile VolatileView2&&) volatile;
+
+ friend void swap(volatile VolatileView2&, volatile VolatileView2&);
+
+ friend int* begin(volatile VolatileView2&);
+ friend int* end(volatile VolatileView2&);
+};
+static_assert(std::ranges::range<volatile VolatileView2>);
+static_assert(std::movable<volatile VolatileView2>);
+static_assert(!std::default_initializable<volatile VolatileView2>);
+static_assert(std::ranges::enable_view<volatile VolatileView2>);
+static_assert(std::ranges::view<volatile VolatileView2>);
+
+// const-volatile view types
+
+struct ConstVolatileView1 : std::ranges::view_base {
+ ConstVolatileView1(const volatile ConstVolatileView1&&);
+ const volatile ConstVolatileView1& operator=(const volatile ConstVolatileView1&&) const volatile;
+
+ friend void swap(const volatile ConstVolatileView1&, const volatile ConstVolatileView1&);
+
+ friend int* begin(const volatile ConstVolatileView1&);
+ friend int* end(const volatile ConstVolatileView1&);
+};
+static_assert(std::ranges::range<const volatile ConstVolatileView1>);
+static_assert(std::movable<const volatile ConstVolatileView1>);
+static_assert(!std::default_initializable<const volatile ConstVolatileView1>);
+static_assert(std::ranges::enable_view<const volatile ConstVolatileView1>);
+static_assert(std::ranges::view<const volatile ConstVolatileView1>);
+
+struct ConstVolatileView2 : std::ranges::view_interface<ConstVolatileView2> {
+ ConstVolatileView2(const volatile ConstVolatileView2&&);
+ const volatile ConstVolatileView2& operator=(const volatile ConstVolatileView2&&) const volatile;
+
+ friend void swap(const volatile ConstVolatileView2&, const volatile ConstVolatileView2&);
+
+ friend int* begin(const volatile ConstVolatileView2&);
+ friend int* end(const volatile ConstVolatileView2&);
+};
+static_assert(std::ranges::range<const volatile ConstVolatileView2>);
+static_assert(std::movable<const volatile ConstVolatileView2>);
+static_assert(!std::default_initializable<const volatile ConstVolatileView2>);
+static_assert(std::ranges::enable_view<const volatile ConstVolatileView2>);
+static_assert(std::ranges::view<const volatile ConstVolatileView2>);
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the fix, LGTM!
Per [range.view]/6, a
view_interface
isn't a base class of itself, soenable_view
should reportfalse
. Also, current implementation strategy handlesconst
but notvolatile
, IIUC cv-qualifiers should be consistent handled.In
enable_view.compile.pass.cpp
, coverage for (const
)volatile
types are added.Drive-by: Remove one unnessary
test_macro.h
inclusion in a test.Fixes #132577.